DataGrid display is empty MVVM Wpf - c#

The problem: the DataGrid display is empty, however I have infos, and my DataGrid received the information but still empty!
My XAML:
<DataGrid x:Name="dataGrid" Grid.Row="1" ItemsSource="{Binding ViewList}"
CanUserAddRows="False" AlternationCount="2"
AlternatingRowBackground="Blue">
<DataGrid.Columns>
<DataGridTextColumn Header="View" Binding="{Binding id}"
Width="2*" IsReadOnly="True" />
<DataGridTemplateColumn Header="Is Enabled" Width="Auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding isEnabled, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
My ViewModel:
public ConfigRoleModel()
{
_viewList = new ObservableCollection<ViewList>(WCFclient.getListOfViews(1));
}
private ObservableCollection<ViewList> _viewList;
public ObservableCollection<ViewList> ViewList
{
get { return _viewList; }
set
{
_viewList = value;
OnPropertyChanged("ViewList");
}
}
ViewList class:
public class ViewList
{
public int id;
public string description;
public string code;
}
This is the result:
How can I fix it ?

By looking at your code:
You should define public property in your ViewList class for binding to work.
set Datacontext to your viewModel.
No isEnabled property in your DataContext
Your ViewList Class should look like this:
public class ViewList
{
public int Id { get; set; }
public bool IsEnabled { get; set; }
...
}
and your Xaml:
<DataGrid x:Name="dataGrid" Grid.Row="1" ItemsSource="{Binding ViewList}"
CanUserAddRows="False" AlternationCount="2" AlternatingRowBackground="Blue" >
<DataGrid.Columns>
<DataGridTextColumn Header="View" Binding="{Binding Id}" Width="2*" IsReadOnly="True" />
<DataGridTemplateColumn Header="Is Enabled" Width="Auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsEnabled , Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
And in your view code behind or in your xaml itself:
Set your DataContext to your ViewModel

Fields are not valid targets for WPF bindings. You should use a property instead.
public class ViewList {
public int Id { get; set; }
public string Description { get; set; }
public string Code { get; set; }
public bool IsEnabled { get; set; }
}

Make sure that your View List class implements INotifyPropertyChanged
public class ViewList : INotifyPropertyChanged
{
private int _id;
public int id
{
get { return _id; }
set
{
_id = value;
OnPropertyChanged(new PropertyChangedEventArgs("id"));
}
}
private string _description;
public string description
{
get { return _description; }
set
{
if((value as string) != null)
{
_description = value;
OnPropertyChanged(new PropertyChangedEventArgs("description"));
}
}
}
private string _code;
public string code
{
get { return _code; }
set
{
_code = value;
OnPropertyChanged(new PropertyChangedEventArgs("code"));
}
}
private bool _isEnabled;
public bool isEnabled
{
get { return _isEnabled; }
set
{
_isEnabled = value;
OnPropertyChanged(new PropertyChangedEventArgs("isEnabled"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
}
Your ViewModel does not need to implement INotifyPropertyChanged if you are just wanting to display data from the ObservableCollection. Here is my ViewModel:
public class MainWindowVM
{
private ObservableCollection<ViewList> _MyList;
public ObservableCollection<ViewList> MyList
{
get { return _MyList; }
set
{
if(value != null)
{
_MyList = value;
}
}
}
public MainWindowVM()
{
_MyList = new ObservableCollection<WpfDataGridTest.ViewList>();
_MyList.Add(new WpfDataGridTest.ViewList() { id = 1, code = "C101", description = "test1", isEnabled = true });
_MyList.Add(new WpfDataGridTest.ViewList() { id = 2, code = "C102", description = "test2", isEnabled = false });
_MyList.Add(new WpfDataGridTest.ViewList() { id = 3, code = "C103", description = "test3", isEnabled = true });
}
}
Here is my Window's XAML
<Window x:Class="WpfDataGridTest.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:local="clr-namespace:WpfDataGridTest"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
DataContext="{StaticResource MainWindowVM}">
<Grid>
<DataGrid x:Name="dataGrid" HorizontalAlignment="Left" Margin="33,34,0,0" VerticalAlignment="Top" Height="236" Width="444"
CanUserAddRows="False" AlternationCount="2" AlternatingRowBackground="Blue" AutoGenerateColumns="False" IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding MyList}">
<DataGrid.Columns>
<DataGridTextColumn Header="View" Binding="{Binding id}" Width="2*" IsReadOnly="True" />
<DataGridTemplateColumn Header="Is Enabled" Width="Auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding isEnabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
<TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="44,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"
Text="{Binding Path=CurrentItem.description, ElementName=dataGrid}"/>
</Window>
This example shows the 3 rows as expected for me using VS 2015. as you can see here:
Note: I renamed your ViewList member of the ViewModel to MyList because I don't like having a member be the same name of a class as it can make things confusing.

Related

TextBlock does not update after assigning the value

I am making a datagrid inside a window, where two columns are DockPanels with a TextBlock and a Button. When a user selects a file/folder, the TextBlock receives the value and displays it. However, it does not reflect it right away in my case. When I double click on the TextBlock, it suddenly shows. What am I missing here?
XAML
<DataGrid Name="copierDataGrid" Margin="30,50,30,100"
ItemsSource="{Binding SelectedAsm}" SelectedItem="{Binding Selected}" AutoGenerateColumns="False" CanUserResizeColumns="False" >
<DataGrid.Columns>
<DataGridTextColumn Header="Prefix" Width="70" Binding="{Binding prefCol, TargetNullValue=''}"/>
<DataGridTemplateColumn Width="235" CanUserResize="False" Header="Select File">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DockPanel DockPanel.Dock="Top">
<TextBlock Name="fileLocColtxtBox" Width="185" Margin="0,0,0,0" TextWrapping="Wrap" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Text="{Binding fileLocCol, TargetNullValue=''}"/>
<Button x:Name="brwsFileBtn" Content="..." Width="30" Margin="0,0,0,0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Click="brwsFileBtn_Click"/>
</DockPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Suffix" Width="70" Binding="{Binding suffCol, TargetNullValue=''}"/>
<DataGridTemplateColumn Header="Target location" Width="*" CanUserResize="False" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DockPanel DockPanel.Dock="Top">
<TextBlock Name="destLocColtxtBox" Width="180" Margin="0,0,0,0" TextWrapping="Wrap" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Text="{Binding destLocCol, TargetNullValue=''}"/>
<Button x:Name="brwsFileBtn" Content="Browse" Width="50" Margin="0,0,0,0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Click="brwsFolderBtn_Click"/>
</DockPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
XAML.CS
public itemAsm Selected { get; set; }
public ObservableCollection<itemAsm> SelectedAsm { get; set; }
public unitCopierFrm()
{
InitializeComponent();
SelectedAsm = new ObservableCollection<itemAsm>();
DataContext= this;}
public class itemAsm
{
public string prefCol { get; set; }
public string fileLocCol { get; set; }
public string suffCol { get; set; }
public string destLocCol { get; set; }
}
}private void brwsFolderBtn_Click(object sender, RoutedEventArgs e)
{
if (Selected !=null)
{
System.Windows.Forms.FolderBrowserDialog dlgFolderBrowser = new System.Windows.Forms.FolderBrowserDialog();
dlgFolderBrowser.Description = "Select the destination folder";
dlgFolderBrowser.RootFolder = System.Environment.SpecialFolder.MyComputer;
dlgFolderBrowser.ShowDialog();
string dirPath = dlgFolderBrowser.SelectedPath;
//if (dlgFolderBrowser.ShowDialog == Windows)
if (dirPath != null)
{
Selected.destLocCol = dirPath;
}
}
}
itemAsm should implement INotifyPropertyChanged and raise the PropertyChanged event to notify the view whenever the data-bound property has been set to a new value:
public class itemAsm : INotifyPropertyChanged
{
private string _prefCol;
public string prefCol
{
get { return _prefCol; }
set { _prefCol = value; NotifyPropertyChanged(); }
}
private string _fileLocCol;
public string fileLocCol
{
get { return _fileLocCol; }
set { _fileLocCol = value; NotifyPropertyChanged(); }
}
private string _suffCol;
public string suffCol
{
get { return _suffCol; }
set { _suffCol = value; NotifyPropertyChanged(); }
}
private string _destLocCol;
public string destLocCol
{
get { return _destLocCol; }
set { _destLocCol = value; NotifyPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
You may also want to consider renaming the properties according to the C# naming conventions.

checkbox in datagrid checked event not trigger wpf mvvm

my checkbox checked event is totally not trigger at all. here is my datagrid code. How should i trigger Checked event in wpf mvvm.
<Window x:Class="EmployeeManager.View.DataGridDownload"
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:ei="http://schemas.microsoft.com/expression/2010/interactions"
Title="DataGridDownload" Height="600" Width="790">
<Grid>
<DataGrid HorizontalAlignment="Left" ItemsSource="{Binding TransView}" AutoGenerateColumns="False" Margin="10,62,0,0" VerticalAlignment="Top" Height="497" Width="762">
<DataGrid.Columns>
<DataGridTextColumn Header="caseRefNo" Binding="{Binding caseRefNo}" />
<DataGridTextColumn Header="subjMatr" Binding="{Binding subjMatr}" />
<DataGridTextColumn Header="Download %" Binding="{Binding incValue}" />
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox
Content="Please Select" IsChecked="{Binding Path=IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<i:InvokeCommandAction Command="{Binding CheckCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding incValue,UpdateSourceTrigger=PropertyChanged}" Background="Red" Foreground="White" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Label Content="{Binding UpdatePercentage}" HorizontalAlignment="Left" Background="Blue" Foreground="White" Margin="10,10,0,0" VerticalAlignment="Top" Width="338" Height="30">
</Label>
<Button Content="Button" HorizontalAlignment="Left" Margin="672,20,0,0" VerticalAlignment="Top" Width="75"/>
</Grid>
</Window>
Here is my view model
public class DataGridDownloadViewModel:BindableBase{
public ObservableCollection<tblTransaction> TransList
{ get; private set; }
public DispatcherTimer dispatchTimer = new DispatcherTimer();
public CollectionView TransView
{ get; private set; }
public DelegateCommand<object> CheckCommand { get; set; }
private String _UpdatePer;
public String UpdatePercentage
{
get { return _UpdatePer; }
set { SetProperty(ref _UpdatePer, value); }
}
private string _caseId;
public string CaseID
{
get { return _caseId; }
set { SetProperty(ref _caseId, value); }
}
private string _isChecked;
public string isChecked
{
get { return _isChecked; }
set { SetProperty(ref _isChecked, value); }
}
private bool CanExecute(object args)
{
return true;
}
private void CheckBoxChecker(object args)
{
//Should Work Here
// Totally not coming to this function
}
public DataGridDownloadViewModel(List<tblTransaction> model)
{
CheckCommand = new DelegateCommand<object>(CheckBoxChecker, CanExecute);
dispatchTimer.Interval = TimeSpan.FromMilliseconds(3000);
dispatchTimer.Tick += dispatchTimer_Tick;
BackGroundThread bgT = Application.Current.Resources["BackGroundThread"] as BackGroundThread;
bgT.GetPercentChanged += (ss, ee) =>
{
UpdatePercentage = bgT.local_percentage.ToString();
};
bgT.GetCaseID += (ss, ee) =>
{
CaseID = bgT.local_caseRef;
};
TransList =new ObservableCollection<tblTransaction>(model);
TransView = GetTransCollectionView(TransList);
TransView.Filter = OnFilterTrans;
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
var cancellationTokenSource = new CancellationTokenSource();
dispatchTimer.Start();
}
private void dispatchTimer_Tick(object sender, EventArgs e)
{
UpdateDataGrid();
}
public void UpdateDataGrid()
{
foreach (tblTransaction tran in TransList)
{
if (tran.caseRefNo == CaseID)
{
tran.incValue = int.Parse(UpdatePercentage);
}
else
{
tran.incValue = tran.incValue;
}
}
TransView.Refresh();
}
bool OnFilterTrans(object item)
{
var trans = (tblTransaction)item;
return true;
}
public CollectionView GetTransCollectionView(ObservableCollection<tblTransaction> tranList)
{
return (CollectionView)CollectionViewSource.GetDefaultView(tranList);
}
}
Is it the correct way of doing it? why checkcommand is not trigger?
Edited
here is my model :
public class tblTransaction
{
public string caseRefNo { get;set;}
public string subjMatr { get; set; }
public int incValue { get; set; }
public DateTime? longTime { get; set; }
public bool IsSelected { get; set; }
}
Thanks
The code below worked for me without using the interactivity library like the answer above, just change the AncestorType property on the Command binding declaration according to your case, for me, I was using a UserControl, on the example above they used Window.
Good Luck!
<DataGridTemplateColumn Width="auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox x:Name="select" HorizontalAlignment="Center"
Command="{Binding DataContext.CheckCommand, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}"
IsChecked="{Binding Path=Selected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
</CheckBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Try the following:
<i:InvokeCommandAction Command="{Binding DataContext.CheckCommand,
RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}" />

Datagrid Combobox Seletected item Value to Textbox binding

Hope you are doing well and coding as Guru.
Here comes Datagrid binding problem which is pain for me for 2 days. I asked other other question related to this problem and solved it, but here is other one.
I have RawVal struct and Signal class(has ObservableCollection).
public struct RawVal
{
public string name { get; set; }
public int value { get; set; }
}
public class Signal
{
public string Name { get; set; }
public Int32 Value { get; set; }
public ObservableCollection<RawVal> rawValue { get; set; }
}
And now XAML looks like
<DataGrid ItemsSource="{Binding}" Name="grdSignal" Grid.Row="1" CanUserAddRows="False" AutoGenerateColumns="False" SelectionChanged="grdSignal_SelectionChanged_1">
<DataGrid.Columns>
<DataGridTextColumn Header=" Signal Name" Binding="{Binding Name}" Width="150"/>
<DataGridTemplateColumn Header=" Physical Value " Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding rawValue}" SelectedItem="Binding name" DisplayMemberPath="name" Name="cmbVal" SelectedIndex="0"
Visibility="{Binding Path=rawValue.Count, Converter={StaticResource ComboBoxItemCountToEnabledConverter}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header=" Value " Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding }" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
UI with binding
Problem is when user selects any item from Physical Value Combobox, value related to this item needs to be shown in Value textbox.
For example, RawVal in Signal class contains these values.
4 - Search Key Trigger
3 - Tailgate Key Trigger
2 - Un-Lock Key Trigger
1 - Lock Key Trigger
0 - No Remote RQ Trigger
When user selects "Tailgate Key Trigger", 3 appears in textbox. When "No Remote RQ Trigger" selected, 0.
Any suggestions and solutions are welcomed,
Thank you.
I have created a simple solution based on MVVM to just guide you how can it be done.
ViewModel class
class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<Signal> _source;
public IEnumerable<Signal> MySource
{
get { return _source; }
}
private RawVal _rawValSelected;
public RawVal RawValSelected
{
get { return _rawValSelected; }
set
{
_rawValSelected = value;
RaisePropertyChanged("RawValSelected");
}
}
public void RaisePropertyChanged(string propName)
{
var pc = PropertyChanged;
if (pc != null)
{
pc(this, new PropertyChangedEventArgs(propName));
}
}
public ViewModel()
{
_source = new ObservableCollection<Signal>
{
new Signal{
Name = "Test",
Value = 1,
rawValue = new ObservableCollection<RawVal>
{
new RawVal{name="Search Key Trigger",value=4},
new RawVal{name="Tailgate Key Trigger",value=3},
new RawVal{name="Un-Lock Key Trigger",value=2},
new RawVal{name="Lock Key Trigger",value=1},
new RawVal{name="No Remote RQ Trigger",value=0}
}
}
};
}
}
The view
<DataGrid ItemsSource="{Binding MySource}" Name="grdSignal" CanUserAddRows="False" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Signal Name" Binding="{Binding Name}" Width="150" />
<DataGridTemplateColumn Header="Physical Value " Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding rawValue}"
SelectedItem="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}},Path=DataContext.RawValSelected}"
DisplayMemberPath="name" SelectedIndex="0" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Value" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}},Path=DataContext.RawValSelected.value}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
I removed some of your code since I don't have the converter code
Note the use of RelativeSource which helps me bind the selected value of the combobox to a property in the view model
The code behind of the view
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
}
You have to have INotifyPropertyChanged event implemented for Signal, and use SelectedItem property of your Combobox to set the value for TextBox
column.
Xaml code :
<DataGrid ItemsSource="{Binding}" Name="grdSignal" Grid.Row="1" CanUserAddRows="False" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header=" Signal Name" Binding="{Binding Name}" Width="150"/>
<DataGridTemplateColumn Header=" Physical Value " Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding rawValue, Mode=OneWay}" SelectedItem="{Binding SelectedRaValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="name" Name="cmbVal"
Visibility="{Binding Path=rawValue.Count, Converter={StaticResource ComboBoxItemCountToEnabledConverter}}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header=" Value " Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding ComboValue}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
C# / code behind
public class Signal: INotifyPropertyChanged
{
public string Name
{
get;
set;
}
public Int32 Value
{
get;
set;
}
private ObservableCollection < RawVal > rawValue1;
public ObservableCollection < RawVal > rawValue
{
get
{
return rawValue1;
}
set
{
rawValue1 = value;
OnPropertyChanged("rawValue");
if (value != null && value.Count > 0)
{
SelectedRaValue = value.First();
}
}
}
private RawVal selectedRaValue;
public RawVal SelectedRaValue
{
get
{
return selectedRaValue;
}
set
{
selectedRaValue = value;
OnPropertyChanged("SelectedRaValue");
ComboValue = value.name;
OnPropertyChanged("ComboValue");
}
}
public string ComboValue
{
get;
set;
}
#region Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Now you can set your DataContext
List < Signal > collection = new List < Signal > ();
collection.AddRange((new[]
{
new Signal
{
Name = "Hello",
Value = 1,
rawValue = new ObservableCollection < RawVal > ((new[] {
new RawVal { name = "A", value = 1},
new RawVal {name = "B", value = 1}
}).ToList()),
},
new Signal {
Name = "World",
Value = 2,
rawValue = new ObservableCollection < RawVal > ((new[] {
new RawVal {name = "A", value = 1},
new RawVal {name = "B", value = 1}
}).ToList()),
}
}).ToList());
this.DataContext = collection;
Hope this helps !!

Binding Property at runtime

I have created an UserControl for reusability in my program:
GroupPanel.xaml:
<UserControl x:Class="View.UserControls.GroupPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"assembly=ViewModel"
Name="root">
<DockPanel>
<DataGrid DockPanel.Dock="Top"
AutoGenerateColumns="False"
CanUserAddRows="True"
CanUserDeleteRows="False"
CanUserReorderColumns="False"
DataContext="{Binding ElementName=root}"
ItemsSource="{Binding DataGridItemSource}"
Name="mainGrid">
<DataGrid.Columns>
<DataGridTextColumn Header="Nr/Unit"
Binding="{Binding Nr}" />
<DataGridTextColumn Header="Text"
Binding="{Binding Text}" />
<DataGridTextColumn Header="Comment"
Binding="{Binding Comment}" />
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Content="Delete" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</DockPanel>
and a Dependency Property to set the DataGrid's ItemsSource:
GroupPanel.xaml.cs:
public static readonly DependencyProperty DataGridItemSourceProperty =
DependencyProperty.Register("DataGridItemSource", typeof(IEnumerable), typeof(GroupPanel),
new PropertyMetadata(null));
public IEnumerable DataGridItemSource
{
get { return (IEnumerable)GetValue(DataGridItemSourceProperty); }
set { SetValue(DataGridItemSourceProperty, value); }
}
I have a window where I need this UserControl 2 times with different ItemsSources to bind. But both Sources have the same properties up to first property. The sources are:
GroupNumber:
Number, int
Text, string
Comment, string
GroupUnit:
Unt, int
Text, string
Comment, string
The behaviour is the same for both of them. Is there a way to set the 1 DataGrid column's binding depending on the ItemsSource?
I have tried something like this:
void GroupPanel_Loaded(object sender, RoutedEventArgs e)
{
var type = DataGridItemSource.GetType();
if(type.FullName.Contains("GroupNumber"))
{
}
}
But I don't know how to set the columns binding inside the if...
Thank you in advance
You can add another dependecy property to your GroupPanel usercontrol:
public string FirstColumnBindingPropertyName
{
get { return (string)GetValue(FirstColumnBindingPropertyNameProperty); }
set { SetValue(FirstColumnBindingPropertyNameProperty, value); }
}
public static readonly DependencyProperty FirstColumnBindingPropertyNameProperty =
DependencyProperty.Register("FirstColumnBindingPropertyName", typeof(string), typeof(GroupPanel), new UIPropertyMetadata(null, new PropertyChangedCallback(OnFirstColumnBindingPropertyNameChanged)));
private static void OnFirstColumnBindingPropertyNameChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
GroupPanel groupPanel = (GroupPanel)dependencyObject;
DataGridTextColumn dataGridTextColumn = groupPanel.mainGrid.Columns[0] as DataGridTextColumn;
if (args.NewValue == null)
{
dataGridTextColumn.Binding = null;
}
else
{
dataGridTextColumn.Binding = new Binding(Convert.ToString(args.NewValue));
}
}
Then in your window you will have:
<local:GroupPanel FirstColumnBindingPropertyName="Number" />
<local:GroupPanel FirstColumnBindingPropertyName="Unt" />
You can use a DataTemplateSelector. Please refer below code.
<Window x:Class="DataTemplateSelector_Learning.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DataTemplateSelector_Learning"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="GroupNumber">
<TextBlock Text="{Binding Number}"/>
</DataTemplate>
<DataTemplate x:Key="GroupUnit">
<TextBlock Text="{Binding Unit}" />
</DataTemplate>
</Window.Resources>
<StackPanel>
<DataGrid DockPanel.Dock="Top"
AutoGenerateColumns="False"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserReorderColumns="False"
Name="numGrid">
<DataGrid.Columns>
<DataGridTemplateColumn Header="GroupNumber">
<DataGridTemplateColumn.CellTemplateSelector>
<local:GroupTemplateSelector
GroupNumber="{StaticResource GroupNumber}"
GroupUnit="{StaticResource GroupUnit}"/>
</DataGridTemplateColumn.CellTemplateSelector>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Text"
Binding="{Binding Text}" />
<DataGridTextColumn Header="Comment"
Binding="{Binding Comment}" />
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Content="Delete" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<DataGrid DockPanel.Dock="Top"
AutoGenerateColumns="False"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserReorderColumns="False"
Name="unitGrid">
<DataGrid.Columns>
<DataGridTemplateColumn Header="GroupUnit">
<DataGridTemplateColumn.CellTemplateSelector>
<local:GroupTemplateSelector
GroupNumber="{StaticResource GroupNumber}"
GroupUnit="{StaticResource GroupUnit}"/>
</DataGridTemplateColumn.CellTemplateSelector>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Text"
Binding="{Binding Text}" />
<DataGridTextColumn Header="Comment"
Binding="{Binding Comment}" />
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Content="Delete" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ObservableCollection<GroupNumber> lstNum = new ObservableCollection<GroupNumber>();
lstNum.Add(new GroupNumber() { Number=1,Text="Test",Comment="Comment"});
numGrid.ItemsSource = lstNum;
ObservableCollection<Groupunit> lstUnit = new ObservableCollection<Groupunit>();
lstUnit.Add(new Groupunit() { Unit = 2, Text = "Test", Comment = "Comment" });
unitGrid.ItemsSource = lstUnit;
}
}
public class GroupTemplateSelector : DataTemplateSelector
{
public DataTemplate GroupNumber
{ get; set; }
public DataTemplate GroupUnit
{ get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is GroupNumber)
{
return GroupNumber;
}
else if (item is Groupunit)
{
return GroupUnit;
}
else
return base.SelectTemplate(item, container);
}
}
public class GroupNumber
{
private int number;
public int Number
{
get { return number; }
set { number = value; }
}
private string text;
public string Text
{
get { return text; }
set { text = value; }
}
private string comment;
public string Comment
{
get { return comment; }
set { comment = value; }
}
}
public class Groupunit
{
private int unit;
public int Unit
{
get { return unit; }
set { unit = value; }
}
private string text;
public string Text
{
get { return text; }
set { text = value; }
}
private string comment;
public string Comment
{
get { return comment; }
set { comment = value; }
}
}

How to bind two dependant ComboBoxes in a DataGrid

I've a problem with the WPF datagrid. I have two ComboBox columns and the second one should set it's datacontext dependant of what is selected in the first one.
ViewModel
public class MyCollection: AbstractViewModel
{
private BindingList<string> _subitems;
public BindingList<string> Subitems
{
get { return _subitems; }
set { _subitems = value; Notify("Subitems");}
}
private string _name;
public string Name
{
get { return _name; }
set { _name = value; Notify("Name");}
}
}
Xaml
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding SelectedName}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox SelectedValuePath="Name"
x:Name="CollectionBox"
SelectedValue="{Binding SelectedName}"
DisplayMemberPath="Name"
ItemsSource="{Binding SomeBinding}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding SelectedSubitem}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox SelectedValue="{Binding SelectedSubitem}"
ItemsSource="{Binding ElementName=CollectionBox, Path=SelectedItem.Subitems}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
The binding for my first ComboBox is ok but I did not see any items in my second ComboBox (the viewmodel has entries for booth of the ComboBoxes); only the TextBox binding works as expected. I tryed it with a ComboBoxColumn as well but with the same result.
here an simple working example how you could use your ViewModel to achieve your needs
XAML
<DataGrid ItemsSource="{Binding Source}" Width="500">
<DataGrid.Columns>
<DataGridTemplateColumn Header="main" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding SelectedName}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox SelectedValuePath="Name"
SelectedValue="{Binding SelectedName}"
SelectedItem="{Binding SelectedMainItem, UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="Name"
ItemsSource="{Binding SomeBinding}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="sub" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding SelectedSubitem}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox SelectedValue="{Binding SelectedSubitem}"
ItemsSource="{Binding Path=SelectedMainItem.Subitems}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
code behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new VMColl();
}
}
public class VMColl
{
List<VM> source;
public List<VM> Source
{
get { return source; }
set { source = value; }
}
public VMColl()
{
source = new List<VM>(){new VM(),new VM(),new VM(),new VM()};
}
}
/// <summary>
/// your Row
/// </summary>
public class VM : Notify
{
private List<mainObj> _items;
public List<mainObj> SomeBinding
{
get { return _items; }
set
{
_items = value;
OnPropertyChanged("Items");
}
}
private string _selectedName;
public string SelectedName
{
get { return _selectedName; }
set
{
_selectedName = value;
OnPropertyChanged("SelectedName");
}
}
private mainObj _selectedMainItem;
public mainObj SelectedMainItem
{
get { return _selectedMainItem; }
set
{
_selectedMainItem = value;
OnPropertyChanged("SelectedMainItem");
OnPropertyChanged("SelectedMainItem.Subitems");
}
}
private string _selectedSubitem;
public string SelectedSubitem
{
get { return _selectedSubitem; }
set
{
_selectedSubitem = value;
OnPropertyChanged("SelectedSubitem");
}
}
public VM()
{
SomeBinding = new List<mainObj>() {new mainObj("first"),new mainObj("second"),new mainObj("someother") };
}
}
public class mainObj : Notify
{
private BindingList<string> _subitems;
public BindingList<string> Subitems
{
get { return _subitems; }
set
{
_subitems = value;
OnPropertyChanged("Subitems");
}
}
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name");
}
}
public mainObj(string name)
{
_name = name;
_subitems = new BindingList<string>(){"1","2","3"};
}
}
public class Notify : INotifyPropertyChanged
{
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
An update of #WiiMaxx answer :
To get selected subitem's value, you have to add a trigger in the second combobox :
<DataTemplate>
<ComboBox SelectedValue="{Binding SelectedSubitem , UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Path=SelectedMainItem.Subitems}" />
</DataTemplate>

Categories

Resources