DataGridColumn binding fail - wpf

This is my DataGrid;
<DataGrid Visibility="Visible"
Grid.Row="1"
SelectionUnit="CellOrRowHeader"
Name="dataGrid"
SelectionMode="Single"
ItemsSource="{Binding collcection}">
<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Command="Copy" Click="MenuItem_Click_1"/>
</ContextMenu>
</DataGrid.ContextMenu>
<DataGrid.Columns>
<DataGridTemplateColumn Header="Select">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox
x:Name="cbRunRobot"
IsChecked="{Binding Value}"
Width="60"
Height="25"
Checked="cbRunRobot_Checked"
Unchecked="cbRunRobot_Unchecked"
Margin="25,0,0,0" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
View Model:
list<MyData> collcection;
public class MyData
{
public string Name;
public string Id;
}
I try to add this Column:
<DataGridColumn Binding="{Binding Name}" Header="Name" Width="180"/>
And got this errors:
Error 1 The type "DataGridColumn" is abstract and must include an explicit value.
Error 2 The member "Binding" is not recognized or is not accessible.

DataGridColumn is an abstract class which means it cannot be instantiated. The same applies to DataGridBoundColumn.
You're choices are:
DataGridCheckBoxColumn for boolean values
DataGridComboBoxColumn for enumerable values
DataGridHyperlinkColumn for Uri values
DataGridTemplateColumn to show any types of data by defining your own cell template
DataGridTextColumn to show text values
It looks like DataGridTextColumn is what you're looking for.

Hi I can suggest you the next:
Bind to ObservableCollection instead the list.
Make your MyData model to implement InotifyPropertyChanged.
Make each binding involved property in MyData model to fire OnPropertyChanged event.
Here is the link to the working example:How to Display and select items in a Datagrid ComboBox with WPF C#, using MVVM.
regards,

Related

Bind WPF DataGridTemplateColumnn ComboBox after page had loaded

I am having problems binding to a DataGridTemplateColumn column on my control. I've read several posts about this, but none seem to be working properly for me, possibly because of the application architecture - I'm not sure. Binding to the DataGridTextColumn works, but the ComboBox I have defined inside the template column is blank. I have this XAML snippet::
<jibcontrols:JibGrid x:Name="UsersDataGrid">
...
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Text="{Binding Path=Role}" ItemsSource="{Binding Item, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}, Mode=FindAncestor}}" DisplayMemberPath="Role"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Width="2*" Binding="{Binding Role}"></DataGridTextColumn>
...
We are using a message bus architecture, so the data source is not ready when the page initially loads. After the data is returned from the service, a method is called to populate the DataGrid:
private void UpdateGrid(Users o)
{
Dispatcher.Invoke(() =>
{
var entities = o.OrderBy(e => e.Role);
UsersDataGrid.FilteredItemsSource = entities;
UsersDataGrid.DataContext = entities;
});
}
What have I gotten wrong in this scenario?
I looked through a tutorial and saw what I was doing wrong. There were a couple things, but how I resolved it was creating a custom class with the values I needed (there were only two):
public class RoleList : List<string>
{
public RoleList()
{
this.Add("Administrator");
this.Add("Operator");
}
}
Next, I added the namespace where the class resided:
xmlns:dm="clr-namespace:Coasters.ViewModels"
I then added a Page.Resources tag to hold my new data source:
<Page.Resources>
<dm:RoleList x:Key="RoleList" />
</Page.Resources>
Last but not least, I actually configured the ComboBox correctly (always a useful thing to do), using the ItemsSource pointing to my data source, and the SelectedItem pointing to the class property that holds the "Role" field:
<DataGridTemplateColumn Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{StaticResource RoleList}" SelectedItem="{Binding Role}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

WPF DataGRidTextColumn binding Visibility is not working [duplicate]

How can I hide a column in a WPF DataGrid through a Binding?
This is what I did:
<DataGridTextColumn Header="Column header"
Binding="{Binding ColumnValue}"
Width="100"
ElementStyle="{StaticResource DataGridRightAlign}"
Visibility="{Binding MyColumnVisibility}" />
And this is what I got (besides the column still visible):
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=MyColumnVisibility; DataItem=null; target element is 'DataGridTextColumn' (HashCode=1460142); target property is 'Visibility' (type 'Visibility')
How to fix the binding?
First of all, DataGridTextColumn (or any other supported dataGrid column) does not lie in the Visual tree of the DataGrid. Hence, by default it doesn't inherit the DataContext of the DataGrid. However, it works for Binding DP only and for no other DP's on DataGridColumn.
Since they don't lie in the same VisualTree, any attempt to get the DataContext using RelativeSource won't work as well because DataGridTextColumn is unable to traverse up to the DataGrid.
There are two other ways to achieve this though:
First using a Freezable class. Freezable objects can inherit the DataContext even when they’re not in the visual or logical tree –We can take advantage of that.
First, create a class inheriting from Freezable and Data DP which we can use to bind in XAML:
public class BindingProxy : Freezable
{
#region Overrides of Freezable
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
#endregion
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object),
typeof(BindingProxy));
}
Now, add an instance of it in DataGrid resources so that it can inherit the DataGrid's DataContext and can bind with its Data DP:
<DataGrid>
<DataGrid.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding}"/>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Visibility="{Binding Data.MyColumnVisibility,
Source={StaticResource proxy}}"/>
</DataGrid.Columns>
</DataGrid>
Second, you can refer to any UI element in XAML using ElementName or x:Reference. However, ElementName works only in the same visual tree, whereas x:Reference doesn't have such constraints.
So, we can use that as well to our advantage. Create a dummy FrameworkElement in XAML with Visibility set to collapsed. The FrameworkElement will inherit the DataContext from its parent container, which can be a Window or UserControl.
And can use that in DataGrid:
<FrameworkElement x:Name="dummyElement" Visibility="Collapsed"/>
<DataGrid>
<DataGrid.Columns>
<DataGridTextColumn Header="Test"
Binding="{Binding Name}"
Visibility="{Binding DataContext.IsEnable,
Source={x:Reference dummyElement}}"/>
</DataGrid.Columns>
</DataGrid>
<Window.Resources>
<ResourceDictionary>
<FrameworkElement x:Key="ProxyElement" DataContext="{Binding}" />
</ResourceDictionary>
</Window.Resources>
<!-- Necessary for binding to resolve: adds reference to ProxyElement to tree.-->
<ContentControl Content="{StaticResource ProxyElement}" Visibility="Collapsed" />
<mch:MCHDataGrid Height="350"
AutoGenerateColumns="False"
FlowDirection="LeftToRight"
ItemsSource="{Binding PayStructures}"
SelectedItem="{Binding SelectedItem}">
<DataGrid.Columns>
<DataGridTemplateColumn Width="70"
Header="name"
IsReadOnly="True"
Visibility="{Binding DataContext.IsShowName,
Source={StaticResource ProxyElement}}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding FieldName}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</mch:MCHDataGrid>
Sample of bound property in view model:
private Visibility _isShowName;
public Visibility IsShowName
{
get { return _isShowName; }
set
{
_isShowName = value;
OnPropertyChanged();
}
}
Another easy solution I like is to add a dummy collapsed FrameworkElement at the same level as the DataGrid. The FrameworkElement can then be used as the Source of the Binding with the x:Reference markup extension.
For example like this:
<FrameworkElement x:Name="FrameWorkElementProxy" Visibility="Collapsed"/>
<DataGrid>
<DataGrid.Columns>
<DataGridTemplateColumn Header="post"
Visibility="{Binding DataContext.DataGridColumnVisibility, Source={x:Reference Name=FrameWorkElementProxy}}"/>
</DataGrid.Columns>
</DataGrid>
Another fast option if you have created the Window/Page/UserControl DataContext object in the XAML like this:
<Window.DataContext>
<local:ViewModel x:Name="MyDataContext"/>
</Window.DataContext>
is that you can add x:Reference using the x:Name of the DataContext object in the Source of the binding:
<DataGridTextColumn Header="Column header"
Binding="{Binding ColumnValue}"
Width="100"
ElementStyle="{StaticResource DataGridRightAlign}"
Visibility="{Binding MyColumnVisibility, Source={x:Reference Name=MyDataContext}}"
That way you can avoid using Binding DataContext.MyColumnVisibility, and just use Binding MyColumnVisibility

Binding Visibility for DataGridColumn in WPF

How can I hide a column in a WPF DataGrid through a Binding?
This is what I did:
<DataGridTextColumn Header="Column header"
Binding="{Binding ColumnValue}"
Width="100"
ElementStyle="{StaticResource DataGridRightAlign}"
Visibility="{Binding MyColumnVisibility}" />
And this is what I got (besides the column still visible):
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=MyColumnVisibility; DataItem=null; target element is 'DataGridTextColumn' (HashCode=1460142); target property is 'Visibility' (type 'Visibility')
How to fix the binding?
First of all, DataGridTextColumn (or any other supported dataGrid column) does not lie in the Visual tree of the DataGrid. Hence, by default it doesn't inherit the DataContext of the DataGrid. However, it works for Binding DP only and for no other DP's on DataGridColumn.
Since they don't lie in the same VisualTree, any attempt to get the DataContext using RelativeSource won't work as well because DataGridTextColumn is unable to traverse up to the DataGrid.
There are two other ways to achieve this though:
First using a Freezable class. Freezable objects can inherit the DataContext even when they’re not in the visual or logical tree –We can take advantage of that.
First, create a class inheriting from Freezable and Data DP which we can use to bind in XAML:
public class BindingProxy : Freezable
{
#region Overrides of Freezable
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
#endregion
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object),
typeof(BindingProxy));
}
Now, add an instance of it in DataGrid resources so that it can inherit the DataGrid's DataContext and can bind with its Data DP:
<DataGrid>
<DataGrid.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding}"/>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Visibility="{Binding Data.MyColumnVisibility,
Source={StaticResource proxy}}"/>
</DataGrid.Columns>
</DataGrid>
Second, you can refer to any UI element in XAML using ElementName or x:Reference. However, ElementName works only in the same visual tree, whereas x:Reference doesn't have such constraints.
So, we can use that as well to our advantage. Create a dummy FrameworkElement in XAML with Visibility set to collapsed. The FrameworkElement will inherit the DataContext from its parent container, which can be a Window or UserControl.
And can use that in DataGrid:
<FrameworkElement x:Name="dummyElement" Visibility="Collapsed"/>
<DataGrid>
<DataGrid.Columns>
<DataGridTextColumn Header="Test"
Binding="{Binding Name}"
Visibility="{Binding DataContext.IsEnable,
Source={x:Reference dummyElement}}"/>
</DataGrid.Columns>
</DataGrid>
<Window.Resources>
<ResourceDictionary>
<FrameworkElement x:Key="ProxyElement" DataContext="{Binding}" />
</ResourceDictionary>
</Window.Resources>
<!-- Necessary for binding to resolve: adds reference to ProxyElement to tree.-->
<ContentControl Content="{StaticResource ProxyElement}" Visibility="Collapsed" />
<mch:MCHDataGrid Height="350"
AutoGenerateColumns="False"
FlowDirection="LeftToRight"
ItemsSource="{Binding PayStructures}"
SelectedItem="{Binding SelectedItem}">
<DataGrid.Columns>
<DataGridTemplateColumn Width="70"
Header="name"
IsReadOnly="True"
Visibility="{Binding DataContext.IsShowName,
Source={StaticResource ProxyElement}}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding FieldName}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</mch:MCHDataGrid>
Sample of bound property in view model:
private Visibility _isShowName;
public Visibility IsShowName
{
get { return _isShowName; }
set
{
_isShowName = value;
OnPropertyChanged();
}
}
Another easy solution I like is to add a dummy collapsed FrameworkElement at the same level as the DataGrid. The FrameworkElement can then be used as the Source of the Binding with the x:Reference markup extension.
For example like this:
<FrameworkElement x:Name="FrameWorkElementProxy" Visibility="Collapsed"/>
<DataGrid>
<DataGrid.Columns>
<DataGridTemplateColumn Header="post"
Visibility="{Binding DataContext.DataGridColumnVisibility, Source={x:Reference Name=FrameWorkElementProxy}}"/>
</DataGrid.Columns>
</DataGrid>
Another fast option if you have created the Window/Page/UserControl DataContext object in the XAML like this:
<Window.DataContext>
<local:ViewModel x:Name="MyDataContext"/>
</Window.DataContext>
is that you can add x:Reference using the x:Name of the DataContext object in the Source of the binding:
<DataGridTextColumn Header="Column header"
Binding="{Binding ColumnValue}"
Width="100"
ElementStyle="{StaticResource DataGridRightAlign}"
Visibility="{Binding MyColumnVisibility, Source={x:Reference Name=MyDataContext}}"
That way you can avoid using Binding DataContext.MyColumnVisibility, and just use Binding MyColumnVisibility

How to get current element in WPF datagrid and how to do something with it?

Some time ago I began to study the MVVM pattern with this tutorial. I use MicroMvvm.
I have a WPF project with EntityFramework model. I wrote ViewModels and XAML views.
I want to display my data in a DataGrid.(2 columns with data and 2 buttoncolumn: Edit, Delete)
<DataGrid Height="250" ItemsSource="{Binding Books}" AutoGenerateColumns="False" >
<DataGrid.Resources>
<DataTemplate x:Key="DeleteTemplate" >
<Button x:Name="DeleteButton" Command="{Binding DeleteBook, Mode=OneWay}" CommandParameter="{Binding}" >Delete</Button>
</DataTemplate>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Title}" Header="Book"/>
<DataGridTextColumn Binding="{Binding Author}" Header="Author"/>
<DataGridTemplateColumn CellTemplate="{StaticResource EditTemplate}" Header="Редактировать"/>
<DataGridTemplateColumn CellTemplate="{StaticResource DeleteTemplate}" Header="Удалить"/>
</DataGrid.Columns>
</DataGrid>
In my LibraryViewModel.cs
#region Commands
void DeleteBookExecute()
{
if (_books == null)
return;
//MessageBox.Show("This is delete button. Delete item id:" myMysticalObjectFromCommandParameter );
}
bool CanDeleteBookExecute()
{
return true;
}
public ICommand DeleteBook
{
get
{
return new RelayCommand(DeleteBookExecute, CanDeleteBookExecute);
}
}
When I press the buttons (delete/edit) I want to to delete/edit the current object.
I don't know how to do it in MVVM.
Can I do it with Command="{Binding DeleteBook, Mode=OneWay}" CommandParameter="{Binding}"?
If it's correct, how I can get the data from CommandParameter in my LibraryViewModel?
As DHN says, your command execution methods DeleteBookExecute(), CanDeleteBookExecute() should have a parameter of type object.
You're ideas are pointing in the right direction. Try this:
<DataGrid Name="LibraryGrid"
Height="250"
ItemsSource="{Binding Books}"
AutoGenerateColumns="False" >
and
Command="{Binding DataContext.DeleteBook, ElementName=LibraryGrid}" CommandParameter="{Binding}"
The use of ElementName with the DataContext.DeleteBook gets you the Command of the LibraryViewModel.
I'm a bit curious that your command methods have no parameters. I would expect them to look like this
void DoSomething(object param) {}
bool CanDoSomething(object param) {}
Nevertheless, I would bind the SelectedItem property of the DataGrid to a property on the VM. For some more information, pls have a look here.
<DataGrid ItemsSource={Binding Books} SelectedItem={Binding SelectedBook} />
So you can easily access the 'current' item in your commands.
Hope this helps a bit.

How to figure out if a DataGrid Row Has been Changed in MVVM?

I want to Bind the WpfDatagrid Rows to a boolean Property in a ViewModel,that shows if Row Has been changed.in the fact I have a datagrid that bind to a class in the model, and have a property IsRowChanged in the ViewModel,and don't know how to bind datagrid to IsRowChanged ?
<DataGrid ItemsSource="{Binding Produts}" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTemplateColumn IsReadOnly="True" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsRowChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
If you use entity framework you can directly extend your entity partial class with this line
public bool IsRowChanged { get { return myEntity.EntityState == EntityState.Modified; } }

Resources