1

I am using Caliburn Micro and Dapper in a WPF Project where I create a DataGrid which I populate with data from a SQL Server Database Table. Please consider the following code snippets:

ChangesModel.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PTSRDesktopUI.Models
{
    //public class for all changes attributes
    public class ChangesModel
    {
        public int ID { get; set; }
        public string Facility { get; set; }
        public string Controller { get; set; }
        public string ParameterName { get; set; }
        public string OldValue { get; set; }
        public string NewValue { get; set; }
        public DateTime ChangeDate { get; set; }
        public bool Validated { get; set; }
        public DateTime ValidationDate { get; set; }
    }
}

OverviewView.xaml

<!--Datagrid Table-->
<DataGrid Grid.Row="1" x:Name="Changes" CanUserAddRows="False" AutoGenerateColumns="False" HorizontalAlignment="Stretch">
    <DataGrid.Columns>
        <DataGridTextColumn CellStyle="{StaticResource DataGridCellCentered}" IsReadOnly="True" 
                            Header="Facility" Binding="{Binding Path=Facility}"/>
        <DataGridTextColumn CellStyle="{StaticResource DataGridCellCentered}" IsReadOnly="True" 
                            Header="Controller" Binding="{Binding Path=Controller}"/>
        <DataGridTextColumn CellStyle="{StaticResource DataGridCellCentered}" IsReadOnly="True" 
                            Header="Parameter" Binding="{Binding Path=ParameterName}"/>
        <DataGridTextColumn CellStyle="{StaticResource DataGridCellCentered}" IsReadOnly="True" 
                            Header="Old Value" Binding="{Binding Path=OldValue}"/>
        <DataGridTextColumn CellStyle="{StaticResource DataGridCellCentered}" IsReadOnly="True" 
                            Header="New Value" Binding="{Binding Path=NewValue}"/>
        <DataGridTextColumn CellStyle="{StaticResource DataGridCellCentered}" IsReadOnly="True" 
                            Header="Changed Date" Binding="{Binding Path=ChangeDate, 
                            StringFormat='{}{0:dd.MM HH:mm}'}"/>
        <DataGridTemplateColumn CellStyle="{StaticResource DataGridCellCentered}" 
                                Header="Validated" IsReadOnly="True">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate >
                    <CheckBox IsChecked="{Binding Path=Validated}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
        <DataGridTextColumn CellStyle="{StaticResource DataGridCellCentered}" IsReadOnly="True" 
                            Header="Validation Date" Binding="{Binding Path=ValidationDate, 
                            StringFormat='{}{0:dd.MM HH:mm}'}"/>
        <DataGridTemplateColumn CellStyle="{StaticResource DataGridCellCentered}" Header="Validate">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Button x:Name="Validate_Btn" cal:Message.Attach="Validate">Validate</Button>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

OverviewViewModel.cs

using Caliburn.Micro;
using PTSRDesktopUI.Helpers;
using PTSRDesktopUI.Models;

namespace PTSRDesktopUI.ViewModels
{
    public class OverviewViewModel : Screen
    {

        //Create new Bindable Collection variable of type ChangesModel
        public BindableCollection<ChangesModel> Changes { get; set; }


        public OverviewViewModel()
        {

            //Create connection to DataAccess class
            DataAccess db = new DataAccess();

            //get the changes from DataAccess function and store them as a bindable collection in Changes
            Changes = new BindableCollection<ChangesModel>(db.GetChanges());

        }

        //Validate_Btn click event
        public void Validate()
        {
           //Some Code        
        }

    }
}

DataAccess.cs

//Function to get all changes from database using stored procedures
public List<ChangesModel> GetChanges()
{
    using (IDbConnection connection = new System.Data.SqlClient.SqlConnection(DBHelper.CnnVal("ptsrDB")))
    {
        var output = connection.Query<ChangesModel>("dbo.getChanges").ToList();
        return output;
    }
}

I use a stored procedure called getChangesto get the data from the SQL Server. The data is displayed and it all works fine. What I want to do now is this: Firstly I want the Validate_Btn to only be visible on rows where the CheckBox is unchecked. Secondly, if the user clicks the Validate_Btn, I want to change the CheckBox to checked, make the button invisible and trigger a new function with a stored procedure from the DataAccess class to update the boolean value for Validated in the database table. Anyone have any ideas how I could do this?

Mysterio
  • 139
  • 12

1 Answers1

1

To solve the first part of displaying and hiding the Button based on whether the CheckBox is checked, you should first implement the INotifyPropertyChanged interface in your ChangesModel class and raise the PropertyChanged event when the Validated property is set:

public class ChangesModel : INotifyPropertyChanged
{
    public int ID { get; set; }
    public string Facility { get; set; }
    public string Controller { get; set; }
    public string ParameterName { get; set; }
    public string OldValue { get; set; }
    public string NewValue { get; set; }
    public DateTime ChangeDate { get; set; }

    private bool _validated;
    public bool Validated
    {
        get { return _validated; }
        set { _validated = value; NotifyPropertyChanged(); }
    }

    public DateTime ValidationDate { get; set; }

    public event PropertyChangedEventHandler PropertyChanged; 
    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

}

You could then bind the Button's Visibility property to the Validated source property and use a converter to convert between the bool value and a Visibility enumeration value:

<DataGridTemplateColumn Header="Validate">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <DataTemplate.Resources>
                <BooleanToVisibilityConverter x:Key="converter" />
            </DataTemplate.Resources>
            <Button x:Name="Validate_Btn" cal:Message.Attach="Validate"
                    Visibility="{Binding Validated, Converter={StaticResource converter}}">Validate</Button>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

You may also want to set the UpdateSourcePropertyTrigger on the CheckBox binding for the source property to be set immediately:

<CheckBox IsChecked="{Binding Path=Validated, UpdateSourceTrigger=PropertyChanged}" />

For your Validate() to get called when you click the button, you could bind the Bind.Model attached property to the view model:

<Button x:Name="Validate"
        cal:Bind.Model="{Binding DataContext, 
            RelativeSource={RelativeSource AncestorType=DataGrid}}">Validate</Button>

Combining the bindings requires to specify a source of the Visibility binding:

<Button x:Name="Validate"
    Visibility="{Binding DataContext.Validated, 
       Converter={StaticResource converter}, RelativeSource={RelativeSource AncestorType=DataGridCell}}"
    cal:Bind.Model="{Binding DataContext, RelativeSource={RelativeSource AncestorType=DataGrid}}">Validate</Button>
mm8
  • 163,881
  • 10
  • 57
  • 88
  • Thanks a lot for your answer. This worked really well with the visibility problem. Do you have any idea how I could toggle the boolean value of the CheckBox in the Validate() function of my ViewModel? I do not want the user to be able to check or uncheck the checkbox in the column, but rather check it through the Validate Button and not be able to uncheck it again. – Mysterio Feb 05 '20 at 13:47
  • @Rei: Can't you just disable the CheckBox then? – mm8 Feb 05 '20 at 13:47
  • I tried that by using `IsReadOnly="True"` in the `DataGridTemplateColumn`, but it does not seem to be working. But still even if I managed to disable it, I still would need to change the `CheckBox` value when the the Validate Button is clicked. – Mysterio Feb 05 '20 at 13:50
  • @Rei: That should just be a matter of setting the corresponding `Validated` property. – mm8 Feb 05 '20 at 13:51
  • 1
    @Rei: Set `IsHitTestVisible` to `false` on the actual `CheckBox` element to disable it. – mm8 Feb 05 '20 at 13:52
  • Yeah true I don't know why I did not think of using `IsHitTestVisible` on the actual element. Thanks. – Mysterio Feb 05 '20 at 13:57
  • About setting the corresponding `Validated` property, should I bind the property to the `Button` using `cal:Message.Attach` or some other Caliburn action, or should I access the property in the `Validate()` method of my ViewModel? – Mysterio Feb 05 '20 at 14:03
  • @Rei: Please ask a new question if you have another issue. This one is broad as-is. – mm8 Feb 05 '20 at 14:04
  • what I am asking is the same as the original issue. Your answer helped me with the Visibility part but still I cannot trigger the CheckBox through the Button. Anyway, maybe I will ask a new question. Thanks again, appreciate your help. – Mysterio Feb 05 '20 at 14:06