SelectedValue binding does not write write back - c#

I'm new to wpf. Trying to add a listbox in a datagrid. Everything runs perfect but the selected value binding is not working. It's not writing the SelectedValue back. Please help.
<DataGrid Name="SoruDataGrid" ItemsSource="{Binding Test.TestSonucuCollection}" Grid.Row="1" AutoGenerateColumns="False" Grid.ColumnSpan="2" >
<DataGrid.Columns>
<DataGridTextColumn Header="Id" Binding="{Binding Soru.Id}"/>
<DataGridTextColumn Header="Soru" Binding="{Binding Soru.Text}" Width="300">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
</DataGridTextColumn.ElementStyle>
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="TextBox">
<Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="AcceptsReturn" Value="true" />
</Style>
</DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>
<DataGridTemplateColumn Header="Cevap">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ListBox SelectionMode="Single"
ItemsSource="{Binding Soru.CevapCollection}"
DisplayMemberPath="Text"
SelectedValuePath="{Binding Id}"
SelectedValue="{Binding CevapId, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}">
CevapModel class
using System.ComponentModel;
namespace Test.Model
{
public class CevapModel : INotifyPropertyChanged
{
Cevap _cevap;
public event PropertyChangedEventHandler PropertyChanged;
public CevapModel()
{
_cevap = new Cevap();
}
public CevapModel(Cevap cevap)
{
_cevap = cevap;
}
public int Id
{
get { return _cevap.Id; }
set
{
_cevap.Id = value;
OnPropertyChanged("Id");
}
}
public int SoruId
{
get { return _cevap.SoruId; }
set
{
_cevap.SoruId = value;
OnPropertyChanged("SoruId");
}
}
public string Text
{
get { return _cevap.Text; }
set
{
_cevap.Text = value;
OnPropertyChanged("Text");
}
}
public int Puan
{
get { return _cevap.Puan; }
set
{
_cevap.Puan = value;
OnPropertyChanged("Puan");
}
}
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
}
TestSonucuModel.cs
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
namespace Test.Model
{
public class TestSonucuModel : INotifyPropertyChanged
{
TestSonucu _testSonucu;
SoruModel _soru;
public event PropertyChangedEventHandler PropertyChanged;
public TestSonucuModel()
{
_testSonucu = new TestSonucu();
}
public TestSonucuModel(TestSonucu testSonucu)
{
_testSonucu = testSonucu;
}
public int Id
{
get { return _testSonucu.Id; }
set
{
_testSonucu.Id = value;
OnPropertyChanged("Id");
}
}
public int TestId
{
get { return _testSonucu.TestId; }
set
{
_testSonucu.TestId = value;
OnPropertyChanged("TestId");
}
}
public int SoruId
{
get { return _testSonucu.SoruId; }
set
{
_testSonucu.SoruId = value;
OnPropertyChanged("SoruId");
}
}
public SoruModel Soru
{
get { return _soru; }
set
{
_soru = value;
OnPropertyChanged("Soru");
}
}
public int CevapId
{
get { return _testSonucu.CevapId; }
set
{
_testSonucu.CevapId = value;
OnPropertyChanged("CevapId");
}
}
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
public static implicit operator TestSonucu(TestSonucuModel t)
{
return t._testSonucu;
}
}
}}

If, as you said, CevapId is a property of TestSonucu class which is the view model behind each row of your DataGrid you don't need to change binding context in this case as it will be set to instance of TestSonucu class
<ListBox ...
ItemsSource="{Binding Soru.CevapCollection}"
DisplayMemberPath="Text"
SelectedValuePath="Id"
SelectedValue="{Binding Path=CevapId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
also SelectedValuePathshould be just Id, in the same way as you specify DisplayMemberPath which means it will take Text property of each CevapCollection item for display and Id property as value

Related

Selected Items implementation in a ListBox acts odd in WPF and MVVM

I made quite research to implement selected items via MVVM in WPF. I thought I had success but now the selection is made according to scroll position. I select all items in the listbox but only first 11 marked as selected. If I scroll more, more selected. If I scroll to the bottom all items selected. Is there solution for this problem?
XAML:
<ListBox x:Name="DataListBox" SelectionMode="Extended" HorizontalAlignment="Left" Margin="5,5,0,0" Grid.Row="1" Grid.Column="0" Grid.RowSpan="8"
VerticalAlignment="Top" Height="200" Width="200"
ItemsSource="{Binding DataListBoxItemsSource, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding DataListBoxSelectedItem, UpdateSourceTrigger=PropertyChanged}"
>
<ListBox.InputBindings>
<KeyBinding Command="ApplicationCommands.SelectAll" Modifiers="Ctrl" Key="A" />
</ListBox.InputBindings>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
</Style>
</ListBox.ItemContainerStyle>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged" >
<i:CallMethodAction TargetObject="{Binding}" MethodName="DataListBox_SelectionChanged"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
View Model
public async void CreateLayersTOC()
{
if (MapView.Active != null)
{
if (DataListBoxSelectedItem != null || FavoriteTabsSelectedItem != null)
MainStackPanelIsEnabled = false;
LayerNames = new List<string>();
await Task.Run(() =>
{
MessageBox.Show("source count " + DataListBoxItemsSource.Count);//58 items all selected
if (DataListBoxSelectedItem != null)
foreach (ItemPresenter itemP in DataListBoxItemsSource)
{
if (itemP.IsSelected)
{
if (LayerNames.Contains(itemP.ToString()) == false)
LayerNames.Add(itemP.ToString());
}
}
if (FavoriteTabsSelectedItem != null)
{
foreach (ItemPresenter itemP in FavListBoxItemsSource)
{
if (itemP.IsSelected)
{
if (LayerNames.Contains(itemP.ToString()) == false)
LayerNames.Add(itemP.ToString());
}
}
}
MessageBox.Show("Coll" + LayerNames.Count);//Count depends on scroll position
});
//do stuff
}
else
MessageBox.Show("Make sure to have a map available before adding layers to a map");
MainStackPanelIsEnabled = true;
}
public class ItemPresenter : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private readonly string _value;
public ItemPresenter(string value)
{
_value = value;
}
public override string ToString()
{
return _value;
}
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
if (_isSelected != value)
{
_isSelected = value;
OnPropertyChanged();
}
}
}
}
Here is a simple example of everything that is necessary to bind the SelectedItem of a ListBox and the IsSelected property of the ListBoxItems.
XAML:
<ListBox ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem}"
SelectionMode="Extended">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding Selected}"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
View Model:
public class DataItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string Name { get; set; }
private bool selected;
public bool Selected
{
get { return selected; }
set
{
selected = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(Selected)));
Debug.WriteLine(Name + " selected: " + selected);
}
}
}
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<DataItem> Items { get; }
= new ObservableCollection<DataItem>();
public IEnumerable<DataItem> SelectedItems
{
get { return Items.Where(i => i.Selected); }
}
private DataItem selectedItem;
public DataItem SelectedItem
{
get { return selectedItem; }
set
{
selectedItem = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedItem)));
}
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var vm = new ViewModel();
vm.Items.Add(new DataItem { Name = "Item 1" });
vm.Items.Add(new DataItem { Name = "Item 2" });
vm.Items.Add(new DataItem { Name = "Item 3" });
vm.Items.Add(new DataItem { Name = "Item 4" });
vm.Items.Add(new DataItem { Name = "Item 5" });
DataContext = vm;
}
}

How to Select All CheckBox of a Column on DataGrid Header CheckBox in WPF DataGrid

Im facing an issue with WPF DataGrid Checkboxes C#.
Im not finding a way to select all cell template checkboxes when the header template checkbox is selected. in viewmodel its working fine. it get select all but in view it no showing any selected checkbox sign/mark on checked header checkbox.The problem I'm stuck with is related to checkbox in DataGrid(WPF)
click this link I want to do same like this
My XAML code :
<DataGrid x:Name="DgLines" ItemsSource="{Binding OpcUaEndpoints}"
MouseDoubleClick="DgLines_MouseDoubleClick" SelectionMode="Extended"
DataContext="{Binding}" IsReadOnly="True" Grid.ColumnSpan="5">
<DataGrid.Columns>
<DataGridTemplateColumn
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<CheckBox Name="ckbSelectedAll" Checked="ckbSelectedAll_Checked" Unchecked="ckbSelectedAll_Unchecked"
IsChecked="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged}"></CheckBox>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Name="cbkSelect" Checked="cbkSelect_Checked" Unchecked="cbkSelect_Unchecked"
IsChecked="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!--<DataGridTextColumn Width="200" Header="Id" Binding="{Binding Id }" />-->
<DataGridTextColumn Width="200" Header="Name" Binding="{Binding Name}"/>
<DataGridTextColumn Width="500" Header="Description" Binding="{Binding Description}"/>
<DataGridTextColumn Width="500" Header="Lines" Binding="{Binding Endpoint}"/>
</DataGrid.Columns>
</DataGrid>
ViewModelCode:
private void ckbSelectedAll_Unchecked(object sender, RoutedEventArgs e)
{
// this.DgLines.UnselectAll();
foreach (AddLinesViewModel c in DgLines.ItemsSource)
{
c.IsSelected = false;
}
}
private static OpcUaEndpointsListViewModel _instance;
private static readonly object Padlock = new object();
private ICommand _addCommand;
private ICommand _uncheckCommand;
private ICommand _searchcommand;
private ObservableCollection<AddOpcUaEndpointsViewModel> _endpoint;
public string _charNameFromTB;
public OpcUaEndpointsListViewModel()
{
BindDataGrid();
}
public static OpcUaEndpointsListViewModel Instance
{
get
{
lock (Padlock)
{
return _instance ?? (_instance = new OpcUaEndpointsListViewModel());
}
}
}
/// <summary>
/// //OPC UA Endpoint List
/// </summary>
public ObservableCollection<AddOpcUaEndpointsViewModel> OpcUaEndpoints
{
get => _endpoint;
set
{
if (OpcUaEndpoints == value)
{
_endpoint = value;
OnPropertyChanged("OpcUaEndpoints");
}
}
}
public string CharNameFromTB
{
get { return _charNameFromTB; }
set
{
_charNameFromTB = value;
OnPropertyChanged("CharNameFromTB");
}
}
public ICommand AddCommand
{
get { return _addCommand ?? (_addCommand = new RelayCommand(p => ExecuteAddCommand())); }
}
public ICommand SearchCommand
{
get { return _searchcommand ?? (_searchcommand = new RelayCommand(p => ExecuteSearchCommand())); }
}
private void ExecuteSearchCommand()
{
BindDataGridsearch();
}
private void BindDataGrid()
{
var opcendptsModel = opcUaEndpointsService.GetAll();
_endpoint = new ObservableCollection<AddOpcUaEndpointsViewModel>(opcendptsModel.Select(p => new AddOpcUaEndpointsViewModel(p)));
}
Please find working code. I have made some modification to your code
XAML
<DataGrid x:Name="DgLines" ItemsSource="{Binding OpcUaEndpoints}" AutoGenerateColumns="False"
SelectionMode="Extended" IsReadOnly="True" Grid.ColumnSpan="5">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<CheckBox Name="ckbSelectedAll"
IsChecked="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked" >
<i:InvokeCommandAction Command="{Binding DataContext.CheckedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}" />
</i:EventTrigger>
<i:EventTrigger EventName="Unchecked" >
<i:InvokeCommandAction Command="{Binding DataContext.UncheckedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Name="cbkSelect"
IsChecked="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!--<DataGridTextColumn Width="200" Header="Id" Binding="{Binding Id }" />-->
<DataGridTextColumn Width="200" Header="Name" Binding="{Binding Name}"/>
<DataGridTextColumn Width="500" Header="Description" Binding="{Binding Description}"/>
<DataGridTextColumn Width="500" Header="Lines" Binding="{Binding Endpoint}"/>
</DataGrid.Columns>
</DataGrid>
c#
public class OpcUaEndpointsListViewModel : INotifyPropertyChanged
{
private static OpcUaEndpointsListViewModel _instance;
private static readonly object Padlock = new object();
private ICommand _addCommand;
//private ICommand _uncheckCommand;
private ICommand _searchcommand;
private ICommand _checkedCommand { get; set; }
private ICommand _unCheckedCommand { get; set; }
private ObservableCollection<AddOpcUaEndpointsViewModel> _endpoint;
public string _charNameFromTB;
public event PropertyChangedEventHandler PropertyChanged;
public OpcUaEndpointsListViewModel()
{
BindDataGrid();
}
public static OpcUaEndpointsListViewModel Instance
{
get
{
lock (Padlock)
{
return _instance ?? (_instance = new OpcUaEndpointsListViewModel());
}
}
}
/// <summary>
/// //OPC UA Endpoint List
/// </summary>
public ObservableCollection<AddOpcUaEndpointsViewModel> OpcUaEndpoints
{
get { return _endpoint; }
set
{
if (OpcUaEndpoints == value)
{
_endpoint = value;
OnPropertyChanged("OpcUaEndpoints");
}
}
}
public string CharNameFromTB
{
get { return _charNameFromTB; }
set
{
_charNameFromTB = value;
OnPropertyChanged("CharNameFromTB");
}
}
public ICommand AddCommand
{
get { return _addCommand ?? (_addCommand = new WpfApplication1.RelayCommand<object>(p => ExecuteAddCommand())); }
}
public ICommand SearchCommand
{
get { return _searchcommand ?? (_searchcommand = new RelayCommand<object>(p => ExecuteSearchCommand())); }
}
public ICommand CheckedCommand
{
get { return _checkedCommand ?? (_checkedCommand = new WpfApplication1.RelayCommand<object>(p => ExecuteCheckedCommand())); }
}
public ICommand UncheckedCommand
{
get { return _unCheckedCommand ?? (_unCheckedCommand = new WpfApplication1.RelayCommand<object>(p => ExecuteUnCheckedCommand())); }
}
private void ExecuteSearchCommand()
{
///BindDataGridsearch();
}
private void ExecuteCheckedCommand()
{
foreach (var item in _endpoint)
{
item.IsSelected = true;
}
}
private void ExecuteUnCheckedCommand()
{
foreach (var item in _endpoint)
{
item.IsSelected = false;
}
}
private void ExecuteAddCommand()
{
///BindDataGridsearch();
}
private void BindDataGrid()
{
_endpoint = new ObservableCollection<AddOpcUaEndpointsViewModel>();
_endpoint.Add(new AddOpcUaEndpointsViewModel { Name = "A", Description = "A", Endpoint = 1 });
_endpoint.Add(new AddOpcUaEndpointsViewModel { Name = "B", Description = "B", Endpoint = 2 });
_endpoint.Add(new AddOpcUaEndpointsViewModel { Name = "C", Description = "C", Endpoint = 3 });
_endpoint.Add(new AddOpcUaEndpointsViewModel { Name = "D", Description = "D", Endpoint = 4 });
_endpoint.Add(new AddOpcUaEndpointsViewModel { Name = "E", Description = "E", Endpoint = 5 });
}
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class AddOpcUaEndpointsViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private string _name;
public string Name
{
get { return _name; }
set { _name = value; OnPropertyChanged("Name"); }
}
private string _description;
public string Description
{
get { return _description; }
set { _description = value; OnPropertyChanged("Description"); }
}
private int _endPoint;
public int Endpoint
{
get { return _endPoint; }
set { _endPoint = value; OnPropertyChanged("Endpoint"); }
}
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set { _isSelected = value; OnPropertyChanged("IsSelected"); }
}
}
The DataGrid column definitions do not inherit the DataContext as they're not part of the visual tree.
You will have to use a BindingProxy¹ to get around this.
<DataGrid.Resources>
<attached:BindingProxy x:Key="proxy" Data="{Binding}"/>
</DataGrid.Resources>
<DataGridTemplateColumn>
<DataGridTemplateColumn.Header>
<CheckBox IsChecked="{Binding Data.IsHeaderCheckBoxChecked, Source={StaticResource proxy}}"/>
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
BindingProxy
public class BindingProxy : Freezable
{
#region XAML Properties
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register(nameof(Data), typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
public object Data
{
get { return GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
#endregion
#region Freezable
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
#endregion
}
Also remember that there are no UI-controls in your VM and no XXX_Clicked handler or smiliar. These belong in the code-behind file (*.xaml.cs), if necessary.
¹ You may also check this question.

Treeview IsExpanded not fires up

I am trying to add to the TreeView the ability to catch the IsExpanded event. So that when a certain Item will expand it will raise a property or command on the view model.
Here is my code:
<TreeView Name="ScenariosTreeView" ItemsSource="{Binding Path=Cat, Mode=TwoWay}" Focusable="True" >
<TreeView.InputBindings>
<KeyBinding Key="Delete" Command="{Binding DeleteCommand}" CommandParameter="{Binding SelectedValue ,ElementName=ScenariosTreeView}" />
</TreeView.InputBindings>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExtended, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type sotc:ScenarioCategory}" ItemsSource="{Binding Path=ScenarioList}" >
<TextBlock Text="{Binding Path=Name}" Foreground="Black" IsEnabled="True" Focusable="True"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type sotc:Scenario}" >
<TextBlock Text="{Binding Path=Name, Mode=TwoWay}" />
</DataTemplate>
</TreeView.Resources>
</TreeView>
I also have a property in the viewmodel for IsExpanded (and for IsSelected) and none of that raises when I am expand the TreeviewItem.
Any Ideas?
Thanks ahead,
Tried to reproduce the issue and implemented attached behavior for calling command when node is expanded and everything works fine. Complete code for the solution:
XAML
<Window x:Class="TreeViewTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sotc="clr-namespace:TreeViewTest"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<sotc:MainWindowViewModel />
</Window.DataContext>
<Grid>
<TreeView Name="ScenariosTreeView" ItemsSource="{Binding Path=Cat, Mode=TwoWay}" Focusable="True" >
<TreeView.InputBindings>
<KeyBinding Key="Delete" Command="{Binding DeleteCommand}" CommandParameter="{Binding SelectedValue ,ElementName=ScenariosTreeView}" />
</TreeView.InputBindings>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExtended, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
<Setter Property="sotc:Behaviours.ExpandingBehaviour" Value="{Binding DataContext.ExpandingCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type sotc:ScenarioCategory}" ItemsSource="{Binding Path=ScenarioList}" >
<TextBlock Text="{Binding Path=Name}" Foreground="Black" IsEnabled="True" Focusable="True"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type sotc:Scenario}" >
<TextBlock Text="{Binding Path=Name, Mode=TwoWay}" />
</DataTemplate>
</TreeView.Resources>
</TreeView>
</Grid>
</Window>
C#
public class ViewModelBase : INotifyPropertyChanged
{
public void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class MainWindowViewModel : ViewModelBase
{
public ICommand ExpandingCommand { get; set; }
private void ExecuteExpandingCommand(object obj)
{
Console.WriteLine(#"Expanded");
}
private bool CanExecuteExpandingCommand(object obj)
{
return true;
}
public MainWindowViewModel()
{
ExpandingCommand = new RelayCommand(ExecuteExpandingCommand, CanExecuteExpandingCommand);
Cat = new ObservableCollection<ScenarioCategory>(new ScenarioCategory[]
{
new ScenarioCategory { Name = "C1" }, new ScenarioCategory { Name = "C2" }, new ScenarioCategory { Name = "C3" }
});
}
public ObservableCollection<ScenarioCategory> Cat { get; set; }
}
public class ScenarioCategory : ViewModelBase
{
string _name;
public string Name
{
get { return _name; }
set { _name = value; OnPropertyChanged(); }
}
bool _isExtended;
public bool IsExtended
{
get { return _isExtended; }
set
{
_isExtended = value;
Console.WriteLine(#"IsExtended set");
OnPropertyChanged();
}
}
bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
Console.WriteLine(#"IsSelected set");
OnPropertyChanged();
}
}
public ObservableCollection<Scenario> ScenarioList { get; set; }
public ScenarioCategory()
{
ScenarioList = new ObservableCollection<Scenario>(new Scenario[] { new Scenario { Name = "1" }, new Scenario { Name = "2" }, new Scenario { Name = "3" } });
}
}
public class Scenario : ViewModelBase
{
string _name;
public string Name
{
get { return _name; }
set { _name = value; OnPropertyChanged(); }
}
bool _isExtended;
public bool IsExtended
{
get { return _isExtended; }
set
{
_isExtended = value;
OnPropertyChanged();
}
}
bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
OnPropertyChanged();
}
}
}
public static class Behaviours
{
public static readonly DependencyProperty ExpandingBehaviourProperty =
DependencyProperty.RegisterAttached("ExpandingBehaviour", typeof(ICommand), typeof(Behaviours),
new PropertyMetadata(OnExpandingBehaviourChanged));
public static void SetExpandingBehaviour(DependencyObject o, ICommand value)
{
o.SetValue(ExpandingBehaviourProperty, value);
}
public static ICommand GetExpandingBehaviour(DependencyObject o)
{
return (ICommand)o.GetValue(ExpandingBehaviourProperty);
}
private static void OnExpandingBehaviourChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TreeViewItem tvi = d as TreeViewItem;
if (tvi != null)
{
ICommand ic = e.NewValue as ICommand;
if (ic != null)
{
tvi.Expanded += (s, a) =>
{
if (ic.CanExecute(a))
{
ic.Execute(a);
}
a.Handled = true;
};
}
}
}
}
public class RelayCommand : ICommand
{
private Action<object> execute;
private Func<object, bool> canExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return this.canExecute == null || this.canExecute(parameter);
}
public void Execute(object parameter)
{
this.execute(parameter);
}
}
Found the answer. It fired the event if it is placed in the object represented as a treeviewItem and not in the view model.

Dynamically update DataGridComboBoxColumn (not MVVM)

I have a DataGrid that its source is DB based. the code is not in MVVM.
Now, i need the DataGridComboBoxColumn source to changed based on a different DataGridComboBoxColumn value thats in the same DataGrid- I'm sure there is a simple solution, but still, couldnt figure it out- how do i do that?
My code:
XAML:
<DataGridComboBoxColumn x:Name="active_idnt_deviceCmb" SelectedValueBinding="{Binding idnt_linked_io_device}" DisplayMemberPath="correct_idnt_active_logic_device" SelectedValuePath="idnt_active_device" Header="input id" Width="80"></DataGridComboBoxColumn>
<DataGridComboBoxColumn x:Name="active_device_addressCmb" ElementStyle="{StaticResource MyComboBoxStyle}" SelectedValueBinding="{Binding idnt_linked_io_device}" DisplayMemberPath="active_device_address" SelectedValuePath="active_device_address" Header="Relay Address" Width="65"><DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="{x:Type ComboBox}">
<EventSetter Event="SelectionChanged" Handler="changeDeviceAddress" />
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
cs:
private void changeDeviceAddress(object sender, SelectionChangedEventArgs e)
{
HelpDataSet.ACTIVE_IO_DEVICESDataTable dtActiveIo = new HelpDataSet.ACTIVE_IO_DEVICESDataTable();
var comboBox = sender as ComboBox;
if (comboBox.SelectedValue != null)
{
AppHelp.ActiveIODeviceAdapter.ClearBeforeFill = true;
AppHelp.ActiveIODeviceAdapter.FillByIdntRelayAddress(dtActiveIo, comboBox.SelectedValue.ToString());
active_idnt_deviceCmb.ItemsSource = dtActiveIo.DefaultView;
}
}
but it changes the whole column source and not just of the specific cell in the row.
here is solution which doesn't use the MVVM approach at all, it based on the WPF selection oriented behavior. Please keep in mind that all models just presents a data grid rows, they does'n have any additional functionality. The Source collection collecting is triggered in the behavior by a combo selection trigger.
Here is the code:
1. XAML code:
<Window x:Class="ComboWithoutCodeBehind.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:comboWithoutCodeBehind="clr-namespace:ComboWithoutCodeBehind"
Title="MainWindow" Height="350" Width="525" x:Name="This">
<Grid >
<DataGrid x:Name="SelectDataGrid"
ItemsSource="{Binding ElementName=This, Path=Persons}" HorizontalAlignment="Left" VerticalAlignment="Top" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridCheckBoxColumn x:Name="dgCheckBox" Header="Select" Width="45" Binding="{Binding IsChecked}"/>
<DataGridTextColumn Header="FIRST NAME" Width="125" Binding="{Binding FNAME}"/>
<DataGridTextColumn Header="LAST NAME" Width="125" Binding="{Binding LNAME}"/>
<DataGridTemplateColumn Header="Selection" Width="120">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<ComboBox Grid.Column="0" x:Name="DataGridTemplateColumnComboBox" SelectedIndex="0" ItemsSource="{Binding ElementName=This, Path=ServersCollection}"
DisplayMemberPath="ServerName"></ComboBox>
<ComboBox Grid.Column="1" DisplayMemberPath="DbName">
<i:Interaction.Behaviors>
<comboWithoutCodeBehind:ItemsSourcePosessingBehavior
SourceProvidingFactory="{Binding ElementName=This, Path=MainSourceProvidingFactory}"
SiblingComboBox="{Binding ElementName=DataGridTemplateColumnComboBox}"/>
</i:Interaction.Behaviors>
</ComboBox>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
2. Code behind, models and factory code:
public partial class MainWindow : Window
{
public static readonly DependencyProperty PersonsProperty = DependencyProperty.Register("Persons",
typeof (ObservableCollection<Person>), typeof (MainWindow),
new PropertyMetadata(default(ObservableCollection<Person>)));
public static readonly DependencyProperty MainSourceProvidingFactoryProperty =
DependencyProperty.Register("MainSourceProvidingFactory", typeof (SourceProvidingFactory),
typeof (MainWindow), new PropertyMetadata(default(SourceProvidingFactory)));
public ObservableCollection<Person> Persons
{
get { return (ObservableCollection<Person>)GetValue(PersonsProperty); }
set { SetValue(PersonsProperty, value); }
}
public SourceProvidingFactory MainSourceProvidingFactory
{
get { return (SourceProvidingFactory)GetValue(MainSourceProvidingFactoryProperty); }
set { SetValue(MainSourceProvidingFactoryProperty, value); }
}
public MainWindow()
{
MainSourceProvidingFactory = new SourceProvidingFactory(GetCollection);
InitSources();
InitializeComponent();
}
private ObservableCollection<DbDetails> GetCollection(object arg)
{
//you can perform your db relate logic here
var sereverDetails = arg as ServerDetails;
return sereverDetails == null ? null : new ObservableCollection<DbDetails>(sereverDetails.DbDetailses);
}
private void InitSources()
{
var l = new List<Person>
{
new Person {FNAME = "John", LNAME = "W"},
new Person {FNAME = "George", LNAME = "R"},
new Person {FNAME = "Jimmy", LNAME = "B"},
new Person {FNAME = "Marry", LNAME = "B"},
new Person {FNAME = "Ayalot", LNAME = "A"},
};
Persons = new ObservableCollection<Person>(l);
ServersCollection = new ObservableCollection<ServerDetails>(new List<ServerDetails>
{
new ServerDetails
{
ServerName = "A",
DbDetailses = new List<DbDetails>
{
new DbDetails {DbName = "AA"},
new DbDetails {DbName = "AB"},
new DbDetails {DbName = "AC"},
}
},
new ServerDetails
{
ServerName = "B",
DbDetailses = new List<DbDetails>
{
new DbDetails {DbName = "BA"},
new DbDetails {DbName = "BB"},
new DbDetails {DbName = "BC"},
}
},
new ServerDetails
{
ServerName = "C",
DbDetailses = new List<DbDetails>
{
new DbDetails {DbName = "CA"},
new DbDetails {DbName = "CB"},
}
}
});
}
public ObservableCollection<ServerDetails> ServersCollection { get; set; }
}
public class SourceProvidingFactory
{
public SourceProvidingFactory(Func<object, ObservableCollection<DbDetails>> action)
{
GetCollection = action;
}
public Func<object, ObservableCollection<DbDetails>> GetCollection { get; set; }
}
public class Person : BaseObservableObject
{
private string _lName;
private string _fName;
private bool _checked;
public bool IsChecked
{
get { return _checked; }
set
{
_checked = value;
OnPropertyChanged();
}
}
public string LNAME
{
get { return _lName; }
set
{
_lName = value;
OnPropertyChanged();
}
}
public string FNAME
{
get { return _fName; }
set
{
_fName = value;
OnPropertyChanged();
}
}
}
public class ServerDetails : BaseObservableObject
{
private string _serverName;
public string ServerName
{
get { return _serverName; }
set
{
_serverName = value;
OnPropertyChanged();
}
}
public List<DbDetails> DbDetailses { get; set; }
}
public class DbDetails : BaseObservableObject
{
private string _dbName;
public string DbName
{
get { return _dbName; }
set
{
_dbName = value;
OnPropertyChanged();
}
}
}
3. Behavior code:
public class ItemsSourcePosessingBehavior : Behavior<ComboBox>
{
public static readonly DependencyProperty SiblingComboBoxProperty = DependencyProperty.Register(
"SiblingComboBox", typeof(ComboBox), typeof(ItemsSourcePosessingBehavior), new PropertyMetadata(default(ComboBox)));
public ComboBox SiblingComboBox
{
get { return (ComboBox)GetValue(SiblingComboBoxProperty); }
set { SetValue(SiblingComboBoxProperty, value); }
}
public static readonly DependencyProperty SourceProvidingFactoryProperty = DependencyProperty.Register(
"SourceProvidingFactory", typeof (SourceProvidingFactory), typeof (ItemsSourcePosessingBehavior), new PropertyMetadata(default(SourceProvidingFactory)));
public SourceProvidingFactory SourceProvidingFactory
{
get { return (SourceProvidingFactory) GetValue(SourceProvidingFactoryProperty); }
set { SetValue(SourceProvidingFactoryProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
SiblingComboBox.SelectionChanged += SiblingComboBoxOnSelectionChanged;
SiblingComboBox.Loaded += SiblingComboBoxOnLoaded;
}
private void SiblingComboBoxOnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
AssociatedObject.ItemsSource = null;
var siblingCombo = sender as ComboBox;
InitAssociatedObjectItemsSource(siblingCombo);
}
private void SiblingComboBoxOnSelectionChanged(object sender, SelectionChangedEventArgs selectionChangedEventArgs)
{
AssociatedObject.ItemsSource = null;
var siblingCombo = sender as ComboBox;
InitAssociatedObjectItemsSource(siblingCombo);
}
private void InitAssociatedObjectItemsSource(ComboBox siblingCombo)
{
if (siblingCombo == null)
{
return;
}
if (SourceProvidingFactory == null)
{
return;
}
AssociatedObject.ItemsSource = SourceProvidingFactory.GetCollection(siblingCombo.SelectedItem);
}
protected override void OnDetaching()
{
base.OnDetaching();
SiblingComboBox.SelectionChanged -= SiblingComboBoxOnSelectionChanged;
SiblingComboBox.Loaded -= SiblingComboBoxOnLoaded;
}
}
This is a complete solution just copy/past...
I'll be glad to help if you will have any problems with the code.
Regards.
I think it's a very basic problem we face in such scenario. now as you don't have problem with display and binding I'll just write down what I think the problem is.
Don't use active_idnt_deviceCmb.ItemsSource this will reset all the Combobox Item Source. You want different option in different rows based on the other column value. So you need to maintain one to one relationship for each record to each dropdown.
The best way to do so is what ever class is binded to per record you need to create that many data sources. if List<MyRecord> is itemsource of grid then design the class like:
public class MyRecord : INotifyPropertyChanged
{
private string a;
public string A
{
get { return a; }
set { a = value;
OnPropertyChanged("A");
OnPropertyChanged("SecondList");
}
}
private string b;
public string B
{
get { return b; }
set { b = value;
OnPropertyChanged("B");
}
}
private List<ComboBoxItem> firstList;
public List<ComboBoxItem> FirstList
{
get { return firstList; }
set { firstList = value;
OnPropertyChanged("FirstList");
}
}
private List<ComboBoxItem> secondList;
public List<ComboBoxItem> SecondList
{
get { return secondList.Where(x=>x.value.StartsWith(A)).ToList(); }
set { secondList = value;
OnPropertyChanged("SecondList");
}
}
}
PS: First column of grid is bind with A, second with B, and firstlist and secondlist is bind to dropdown of both columns. every time I select the value in first column it will reflected back to property A and at that point I'm refreshing the secondlist(which also contain filter logic also, or you can call filter logic and refresh secondlist separately).
As this second list is bind with only one record only one dropdown will reset.
XAML Code for understanding:
<DataGridComboBoxColumn x:Name="active_idnt_deviceCmb"
SelectedValuePath="value"
DisplayMemberPath="display"
SelectedValueBinding="{Binding A}"
Header="input id" Width="80">
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding Path=FirstList}" />
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
<DataGridComboBoxColumn
SelectedValuePath="value"
DisplayMemberPath="display"
SelectedValueBinding="{Binding B}"
Header="Relay Address" Width="65">
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding Path=SecondList}" />
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
Combobox item source class(just to understand DataGridComboBoxColumn binding):
public class ComboBoxItem
{
public ComboBoxItem(string a)
{
display = value = a;
}
public string display { get; set; }
public string value { get; set; }
}
hopefully You'll be able to fit this code in your application. The main thing is One to One relationship that is important.

WPF CommandParameter binding fails in ToolBar?

Here's the situation: It's a bit hard to describe, so skip to the steps to recreate and copy/paste the code into a new project if you want. ListViewModel contains a list of ViewModel (Items) and a list of ICommand (Actions). MainWindow has a ToolBar that binds to the Actions, a ListView that binds to the Items, and a ContextMenu that binds to the actions within the ListView. In my implementation of ICommand (Command.cs), I have added the ability to insert custom code (the OnIsVisible property) that checks if the command Visibility should be Visible or Collapsed. This code works great for the Actions in the ToolBar and ContextMenu until you open the ContextMenu. Then the CommandParameter for the ToolBar is forever null, except when the ContextMenu is open.
Steps to recreate:
Select an item in the ListView
Click "Show When Selected" in the ContextMenu
Select another item in the ListView
At this point, the CommandParameter binding will always be NULL to the command object. So the "Show When Selected" button in the ToolBar will no longer appear.
Code:
In a new WPF application project called "NullParameter", create/edit the following files...
MainWindow.xaml:
<Window x:Class="NullParameter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="IsSelected"
Value="{Binding IsSelected}" />
</Style>
<Style x:Key="contextMenuItemStyle"
TargetType="{x:Type MenuItem}">
<Setter Property="Header"
Value="{Binding Header}" />
<Setter Property="Command"
Value="{Binding}" />
<Setter Property="Visibility"
Value="{Binding Visibility}" />
<Setter Property="CommandParameter"
Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget.Tag.SelectedItems}" />
</Style>
<DataTemplate x:Key="toolBarActionItemTemplate">
<Button Content="{Binding Header}"
Command="{Binding}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ToolBar}, Path=Tag.SelectedItems}"
Visibility="{Binding Visibility}" />
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.Children>
<ToolBar Grid.Row="0"
ItemsSource="{Binding Actions}"
ItemTemplate="{StaticResource toolBarActionItemTemplate}"
Tag="{Binding}" />
<ListView Grid.Row="1"
ItemsSource="{Binding Items}"
SelectionMode="Extended"
Tag="{Binding}">
<ListView.ContextMenu>
<ContextMenu ItemsSource="{Binding Actions}"
ItemContainerStyle="{StaticResource contextMenuItemStyle}" />
</ListView.ContextMenu>
<ListView.View>
<GridView>
<GridViewColumn Header="Id"
DisplayMemberBinding="{Binding Id}"/>
</GridView>
</ListView.View>
</ListView>
</Grid.Children>
</Grid>
</Window>
CommandBase.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;
namespace NullParameter
{
public abstract class CommandBase : ICommand, INotifyPropertyChanged
{
private Visibility _visibility;
private string _error;
public Visibility Visibility
{
get { return _visibility; }
protected set
{
if (_visibility == value)
return;
_visibility = value;
if (PropertyChanged == null)
return;
PropertyChanged(this, new PropertyChangedEventArgs("Visibility"));
}
}
public string Error
{
get { return _error; }
set
{
if (_error == value)
return;
_error = value;
if (PropertyChanged == null)
return;
PropertyChanged(this, new PropertyChangedEventArgs("Error"));
}
}
public bool CanExecute(object parameter)
{
Error = DoCanExecute(parameter);
Visibility = DoIsVisible(parameter) ? Visibility.Visible : Visibility.Collapsed;
return Error == null;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
DoExecute(parameter);
}
protected abstract string DoCanExecute(object parameter);
protected abstract bool DoIsVisible(object parameter);
protected abstract void DoExecute(object parameter);
public event PropertyChangedEventHandler PropertyChanged;
}
}
Command.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
namespace NullParameter
{
public class Command : CommandBase
{
public Action OnExecute { get; set; }
public Func<bool> OnIsVisible { get; set; }
public Func<string> OnCanExecute { get; set; }
public string Header { get; set; }
protected override string DoCanExecute(object parameter)
{
if (OnCanExecute == null)
return null;
return OnCanExecute();
}
protected override bool DoIsVisible(object parameter)
{
if (OnIsVisible == null)
return true;
return OnIsVisible();
}
protected override void DoExecute(object parameter)
{
if (OnExecute == null)
return;
OnExecute();
}
}
public class Command<T> : CommandBase
where T : class
{
public Action<T> OnExecute { get; set; }
public Func<T, bool> OnIsVisible { get; set; }
public Func<T, string> OnCanExecute { get; set; }
public string Header { get; set; }
protected T Convert(object parameter)
{
if (parameter == null)
Console.WriteLine("NULL");
return parameter as T;
}
protected override string DoCanExecute(object parameter)
{
if (OnCanExecute == null)
return null;
var p = Convert(parameter);
if (p == null)
return "Invalid Parameter";
return OnCanExecute(p);
}
protected override bool DoIsVisible(object parameter)
{
if (OnIsVisible == null)
return true;
var p = Convert(parameter);
if (p == null)
return false;
return OnIsVisible(p);
}
protected override void DoExecute(object parameter)
{
if (OnExecute == null)
return;
var p = Convert(parameter);
if (p == null)
return;
OnExecute(p);
}
}
}
ListViewModel.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace NullParameter
{
public class ListViewModel
{
public IList<ViewModel> Items { get; private set; }
public IList SelectedItems { get; private set; }
public IList<ICommand> Actions { get; private set; }
public ListViewModel()
{
var items = new ObservableCollection<ViewModel>()
{
new ViewModel()
{
Id = 1
},
new ViewModel()
{
Id = 2
},
new ViewModel()
{
Id = 3
},
};
Items = items;
SelectedItems = items;
Actions = new List<ICommand>()
{
new Command()
{
OnExecute = ShowAlways,
Header = "Show Always"
},
new Command<IList<ViewModel>>()
{
OnExecute = ShowWhenSelected,
OnIsVisible = (list) => { return list.Count(o => o.IsSelected) > 0; },
Header = "Show When Selected"
}
};
}
public void ShowAlways()
{
Console.WriteLine("ShowAlways()");
}
public void ShowWhenSelected(IList<ViewModel> viewModels)
{
Console.WriteLine("ShowWhenSelected({0})", String.Join(",", viewModels.Where(o => o.IsSelected).Select(o => o.Id)));
}
}
}
ViewModel.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NullParameter
{
public class ViewModel : INotifyPropertyChanged
{
private bool _isSelected;
private int _id;
public event PropertyChangedEventHandler PropertyChanged;
public int Id
{
get { return _id; }
set
{
if (_id == value)
return;
_id = value;
if (PropertyChanged == null)
return;
PropertyChanged(this, new PropertyChangedEventArgs("Id"));
}
}
public bool IsSelected
{
get { return _isSelected; }
set
{
if (_isSelected == value)
return;
_isSelected = value;
if (PropertyChanged == null)
return;
PropertyChanged(this, new PropertyChangedEventArgs("IsSelected"));
}
}
}
}
So after reading a few posts about how glitchy the CommandParameter DependencyProperty is, I've given up on using it entirely. Instead, I simply construct my Command objects by passing in the list of selected items in my ListViewModel. Then in the CanExecute and Execute method, I use the stored list of selected items instead of the .NET supplied parameter.
Although this provides a workable solution, it does not necessarily solve the problem posed by the initial question. So I will leave this here as a suggestion for anyone else unfortunate enough to have these issues.

Categories