WPF checkbox IsChecked two way binding not working - c#

the following code I wrote for two way binding. The UI gets updated when anything from code changes but vice versa doesn't work, the UI doesn't change the data code when the checkbox is clicked by the user. Appreciate if anybody sheds some light on the solution.
XAML Code
<DataGrid ItemsSource="{Binding StatusItems}" Name="DataGridUploadingRevitFiles" Margin="5"
IsReadOnly="False" SelectionMode="Single" CanUserAddRows="True"
AutoGenerateColumns="False" SelectionUnit="Cell" Height="Auto">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Update" Width=".5*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Width="200"
IsChecked="{Binding Path=IsUpdateAbleFile,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Model [FamilyStatusItem.cs]
public class FamilyStatusItem : INotifyPropertyChanged
{
private bool _isUpdateAbleFile;
public bool IsUpdateAbleFile
{
get => this._isUpdateAbleFile;
private set
{
this._isUpdateAbleFile = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
ViewModel [FamilyStatusViewItem.cs]
public class FamilyStatusViewItem
{
public ObservableCollection<FamilyStatusItem> StatusItems { get; set; }
public FamilyStatusViewItem()
{
this.StatusItems = new ObservableCollection<FamilyStatusItem>();
}
}

Your setter is private which means it can’t be called from the outside. Thus when you tick or untick the checkbox it can’t be called and the property retains the old state.
Solution: Remove the private modifier.

Try to use public setter
public bool IsUpdateAbleFile
{
get => this._isUpdateAbleFile;
set
{
this._isUpdateAbleFile = value;
OnPropertyChanged();
}
}

You are having a private setter
private set
{
this._isUpdateAbleFile = value;
OnPropertyChanged();
}
Change this to a public, then it should work.

Related

Update DataGrid Checkbox State By Property that Depends on Other Property

I have a model, which contains CanDrawProperty. I want to bind this property to IsEnabled property of DataGrid CheckBox:
public class Series : INotifyPropertyChanged
{
private ObservableCollection<DropPhoto> _dropPhotosSeries;
public ObservableCollection<DropPhoto> DropPhotosSeries
{
get
{
return _dropPhotosSeries;
}
set
{
_dropPhotosSeries = value;
OnPropertyChanged(new PropertyChangedEventArgs("DropPhotosSeries"));
}
}
private bool _canDrawPlot;
public bool CanDrawPlot
{
get
{
if (_dropPhotosSeries?.Where(x => x.Drop.RadiusInMeters != null).ToList().Count > 1)
{
_canDrawPlot = true;
return _canDrawPlot;
}
_canDrawPlot = false;
return _canDrawPlot;
}
set
{
_canDrawPlot = value;
OnPropertyChanged(new PropertyChangedEventArgs("CanDrawPlot"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
}
I want to update my Datagrid CheckBox IsEnabled state based on CanDrawPlot property of this model. But this doesn't seem to work. XAML for DataGrid:
<DataGrid IsReadOnly="True" AutoGenerateColumns="False" SelectionMode="Single" Margin="0" BorderThickness="0" ClipToBounds="True" ItemsSource="{Binding User.UserSeries}" SelectionChanged="SeriesDataGrid_OnSelectionChanged" Name="SeriesDataGrid">
<DataGrid.Columns>
<DataGridTemplateColumn CanUserResize="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsEnabled="{Binding CanDrawPlot}" Checked="ChooseSeries_Checked" x:Name="ChooseSeries" Height="25"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
I have class User. It contains UserSeries. UserSeries has property CanDrawPlot:
public class User : INotifyPropertyChanged
{
private ObservableCollection<Series> _userSeries;
public ObservableCollection<Series> UserSeries
{
get
{
return _userSeries;
}
set
{
_userSeries = value;
OnPropertyChanged(new PropertyChangedEventArgs("UserSeries"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
}
You need to add the following lines to your code
In the xaml:
You need to add to bindinig the UpdateSourceTrigger
<CheckBox IsEnabled="{Binding CanDrawPlot,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Checked="ChooseSeries_Checked" x:Name="ChooseSeries" Height="25"/>
And in the xaml.cs you need to creat an instance of Series class
For example:
private Series series = new Series ();
And in the constructor of the xaml.cs you need to write:
DataContext = series;
Successfully.

WPF: Two way binding does not set source

My two way binding is just working from the source to the TextBox - I can see the default value in the TextBox and even the new value when I change it from the code-behind, but when I change the Text in the TextBox the value doesn't get updated in the Model, even after the TextBox loses focus. The DataContext is also set.
Version.Set doesn't even get called - tested by setting a breakpoint.
XAML:
<DataGrid ItemSource="{Binding Issues}">
<DataGrid.RowDetailsTemplate>
<TextBox Text="{Binding Path=TestReport.Version, Mode=TwoWay}"/>
</DataGrid.RowDetailsTemplate>
</DataGrid>
Models:
public class TestIssue
{
public JiraIssue Issue { get; set; }
public TestReport TestReport { get; set; }
}
public class TestReport : INotifyPropertyChanged
{
private string version = "Defalut Value";
public string Version
{
get => this.version;
set
{
if (value == this.version) return;
this.version = value;
this.OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Code Behind:
public partial class MainWindow : Window
{
public ObservableCollection<TestIssue> Issues { get; set; } = new ObservableCollection<TestIssue>();
public MainWindow()
{
this.DataContext = this;
this.InitializeComponent();
}
}
EDIT: Explicitly setting the UpdateSourceTrigger works, even setting it to FocusLost, which confuses me even more.
First, your XAML code is incorrect and it should be like this:
<DataGrid ItemsSource="{Binding Issues}">
<DataGrid.RowDetailsTemplate>
<ItemContainerTemplate >
<TextBox Text="{Binding Path=TestReport.Version, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</ItemContainerTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
You cannot place <TextBox/> element inside <DataGrid.RowDetailsTemplate> directlyand it should be placed inside <ItemContainerTemplate >.
To update the TextBox you need to tell TextBox element when it should update its value when the source changes by adding UpdateSourceTrigger=PropertyChanged to the binding script as shown in the above code.

How come my databinding is writing out the Length property?

So I've setup a viewmodel to where it binds an ObservableCollection<string> to my DataGrid.
It prints out the value just fine but it also prints out the Length of the property? I don't recall ever setting that in any binding whatsoever. Why is it doing that?
My MainWindow.cs
public MainWindow()
{
InitializeComponent();
DataContext = new MasterViewModel();
}
MasterViewModel.cs
class MasterViewModel
{
public Users Users { get; } = new Users();
public Achievements Achievements { get; } = new Achievements();
}
Users.cs
class Users : INotifyPropertyChanged
{
public Users()
{
newList.Add("Hello there");
}
private ObservableCollection<string> newList = new ObservableCollection<string>();
public ObservableCollection<string> NewList
{
get { return newList; }
set { newList = value; }
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML
<DataGrid ItemsSource="{Binding Users.NewList}" Width="400" Height="200" Margin="182,158,210,61">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"></TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
DataGrid has property AutoGenerateColumns which is set to True by default and makes DataGrid to create a column for each property defined in items.
DataGrid is bound to NewList which contains items of type string which has Length property. So it makes Length column
you can disable auto-generation by setting <DataGrid AutoGenerateColumns="False" ...
I forgot to add the property AutoGenerateColumns="False".
Not sure why it was set to true by default or why it woudl choose the length property of all the properties but I guess I got it fixed.

WPF Binding to Property of Element of ObservableCollection

I'm working with a DataGrid in WPF and I'm trying to perform some data binding that it a little more complex than I'm used to. I have an ObservableCollection of a class that also implements an ObservableCollection of a subclass. I'm trying to bind the IsChecked property of a CheckBox to a value on that subclass and no matter when I try I can't seem to get it to work. Hopefully I'm just missing something simple.
In my main program I have the following, and it works fine for detecting changes to "SomeProperty" on the "MyDevice" class:
ObservableCollection<MyDevice> MyDevices = new ObservableCollection<MyDevice>();
DevicesGrid.ItemSource = MyDevices;
My class definition is below:
public class MyDevice : INotifyPropertyChanged
{
public class Input : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void RaisePropertyChanged([CallerMemberName] string PropertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
private bool _SyncDetected;
public bool SyncDetected
{
get { return _SyncDetected; }
set { _SyncDetected = value; RaisePropertyChanged(); }
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void RaisePropertyChanged([CallerMemberName] string PropertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
private bool _SomeProperty;
public bool SomeProperty
{
get { return _SomeProperty; }
set { _SomeProperty = value; RaisePropertyChanged(); }
}
public ObservableCollection<Input> MyInputs = new ObservableCollection<Input>() { new Input(), new Input() };
}
And this is my XAML:
<DataGrid x:Name="DevicesGrid" Margin="10,80,10,10" AutoGenerateColumns="False">
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}" BasedOn="{StaticResource {x:Type DataGridRow}}">
<Setter Property="ContextMenu" Value="{StaticResource DeviceRowContextMenu}"/>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTemplateColumn Header="Sync/Hotplug" IsReadOnly="True" Width="Auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="2,2,2,2" VerticalAlignment="Center" HorizontalAlignment="Center">
<CheckBox Margin="2,2,2,2" IsHitTestVisible="False" IsChecked="{Binding MyInputs[0].SyncDetected}" Content="In1"/>
<CheckBox Margin="2,2,2,2" IsHitTestVisible="False" IsChecked="{Binding MyInputs[1].SyncDetected}" Content="In2"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
I'm really new at working with WPF so any help is appreciated. Thanks.
Here is something wrong:
public ObservableCollection<Input> MyInputs = new ObservableCollection<Input>() { new Input(), new Input() };
MyDevice.MyInputs is a field, not a property, so the binding system cannot find it through reflection.

ComboBox in datagrid not updated

I have a DataGrid which displays a list of objects. One of the properties in the objects is another custom object. This object is shown as a ComboBox in the grid. When I change the selected item in the ComboBox from the grid, everything seems to work as I expect. However, when I change the selected item from code behind, the SelectedItem in the ComboBox doesn't update. I have implemented the INotifyPropertyChanged and the event is firing as it should. I have also tried to print the domain name in a TextBox and it shows the correct value. The problem I have is that the SelectedItemBinding doesn't seem to work when I update from code behind. Can anyone explain why? Here is my code:
XAML
<DataGrid AutoGenerateColumns="False" Height="506" HorizontalAlignment="Left" Name="EntityGrid" VerticalAlignment="Top" Width="360" Margin="10,62,0,0">
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridCell}">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="DataGridCell_PreviewMouseLeftButtonDown" />
<EventSetter Event="PreviewTextInput" Handler="DataGridCell_PreviewTextInput" />
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding dbName, Mode=TwoWay}" />
<DataGridComboBoxColumn x:Name="Domain" Header="Domain" SelectedItemBinding="{Binding Domain, Mode=TwoWay}" DisplayMemberPath="DomainName}"/>
<DataGridCheckBoxColumn Header="Static" Binding="{Binding Static}" IsReadOnly="True" />
</DataGrid.Columns>
</DataGrid>
My objects
public class Entity : INotifyPropertyChanged
{
private string _dbName;
public string dbName { get { return _dbName; } set { _dbName = value; NotifyPropertyChanged("dbName"); } }
public string EntityName { get; set; }
private Domain _domain;
public Domain Domain
{
get { return _domain; }
set
{
_domain = value;
NotifyPropertyChanged("Domain");
}
}
public bool Static { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class Domain : INotifyPropertyChanged
{
public string DomainName { get; set; }
public string ContextName { get; set; }
public string FileName { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Init code
List<Domain> domains = getDomainsFromConfig();
List<Entity> Entities = dal.getAllEntities(config.Paths.dbml.AllEntities, config.Paths.dbml.LockedEntities);
EntityGrid.ItemsSource = Entities;
Domain.ItemsSource = domains;
Update code
foreach (Entity entity in Entities)
{
entity.Domain = getDefaultDomain();
}
Solved the problem after a couple hours of head ache. In my update code, I created a new instance of Domain. My guess is that it made it inequal to the objects in the current itemsource. My solution to the problem was to select the Domain from the original Domain ItemSource and thereafter assign it to the Entity.
try this:
change the list to ObservableCollection
List<Domain> to ObservableCollection<Domain>
change the combobox to
<DataGrid>
<DataGrid.Columns>
<DataGridTemplateColumn Header="domain">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox SelectedValue="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="Name"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
cause the Domain object have many properties you should set the selected value to the prmary key of the Domain object and the displaymemberpath to the value that you want to show - i think in you case the Domain Name.

Categories