How to change IsReadOnly by Trigger - wpf

can anybody enlight me way the following doesn't work
i'm using the mvvm pattern
after executing my code i'm still able to check and uncheck the Checkboxes in my DataGridCheckBoxColumn
Property (part of my ViewModel)
public bool noCheckPermission
{
get { return false; } // just as example
}
Xaml (my View no code behind)
<DataGrid Height="300" AutoGenerateColumns="False" SelectionMode="Single"
ItemsSource="{Binding Itemlist, UpdateSourceTrigger=PropertyChanged}">
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridCheckBoxColumn}">
<Style.Triggers>
<DataTrigger Binding="{Binding noCheckPermission}" Value="False">
<Setter Property="IsReadOnly" Value="True"/>
</DataTrigger>
<DataTrigger Binding="{Binding noCheckPermission}" Value="True">
<Setter Property="IsReadOnly" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridCheckBoxColumn Width="60"
Header="Freigabe" Binding="{Binding FreigegebenL}" CanUserReorder="False">
</DataGridCheckBoxColumn>
</DataGrid.Columns>
</DataGrid>
Edit:
after doing what blindmeis and Will suggested. I got this as my current XAML
<DataGrid Height="300" Margin="12" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False"
CanUserResizeColumns="False" CanUserResizeRows="False" CanUserReorderColumns="False"
x:Name="grd" ItemsSource="{Binding Itemlist, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding selectedItem, UpdateSourceTrigger=PropertyChanged}" SelectionMode="Single" SelectionChanged="DataGrid_SelectionChanged" TabIndex="2">
<DataGrid.Columns>
<DataGridCheckBoxColumn Width="60" IsReadOnly="{Binding DataContext, Path=noCheckPermission, ElementName=grd}" Binding="{Binding FreigegebenL}"
Header="Freigabe" CanUserReorder="False">
</DataGridCheckBoxColumn>
</DataGrid.Columns>
</DataGrid>
and this as Error in my Output Window:
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=noCheckPermission; DataItem=null; target element is 'DataGridCheckBoxColumn' (HashCode=35155182); target property is 'IsReadOnly' (type 'Boolean')

first
i noticed that my property was in the wrong VM (it was in the Child VM which are my rows) fixing this doesn't solved my problem
finally
after reading this article i was able to solve it
in condensed form
class
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); }
}
// Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
XAML
Resources
<DataGrid.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding}" />
</DataGrid.Resources>
Column
<DataGridCheckBoxColumn Width="60" Header="Freigabe" CanUserReorder="False"
IsReadOnly="{Binding Data.noCheckPermission, Source={StaticResource proxy}}"
Binding="{Binding FreigegebenL}"/>

if you just want the DataGridCheckBoxColumn to be readonly, you should add your style to your DataGridCheckBoxColumn.
<DataGrid Height="300" AutoGenerateColumns="False" SelectionMode="Single"
x:Name="grd"
ItemsSource="{Binding Itemlist, UpdateSourceTrigger=PropertyChanged}">
<DataGrid.Columns>
<DataGridCheckBoxColumn Width="60"
Header="Freigabe" Binding="{Binding FreigegebenL}" CanUserReorder="False">
<DataGridCheckBoxColumn.Style>
<Style TargetType="{x:Type DataGridCheckBoxColumn}">
<Style.Triggers>
<DataTrigger Binding="{Binding DataContext.noCheckPermission, ElementName=grd}" Value="False">
<Setter Property="IsReadOnly" Value="True"/>
</DataTrigger>
<DataTrigger Binding="{Binding DataContext.noCheckPermission, ElementName=grd}" Value="True">
<Setter Property="IsReadOnly" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridCheckBoxColumn.Style>
</DataGridCheckBoxColumn>
</DataGrid.Columns>
</DataGrid>

Related

Change Data bound WPF Datagrid row background/foreground color depending on the boolean property of an object

I have a Datagrid in WPF application (Using MVVM {Caliburn Micro}) which is bounded to property of type ObservableCollection<Student> where Student class looks like this:
public class Student
{
public int ID { get; set; }
public String FullName { get; set; }
public bool Passed { get; set; }
}
Depending on the fact - student passed the exam or not - I want to change the Background/Foreground of the row of the corresponding student to red (if didn't passed).
Below is shown my DataGrid:
<DataGrid Grid.Column="1"
RowBackground="White"
Visibility="Visible"
Grid.Row="15"
ColumnWidth="auto"
IsReadOnly="False"
AutoGenerateColumns="False"
BorderBrush="{StaticResource GridBorder}"
VerticalScrollBarVisibility="Auto"
HorizontalGridLinesBrush="LightGray"
HorizontalScrollBarVisibility="Disabled"
VerticalGridLinesBrush="LightGray"
Name="Students"
CanUserAddRows="True"
BorderThickness="0.8"
SelectionUnit="FullRow"
cal:Message.Attach="[Event MouseDoubleClick] = [Action GetRow($dataContext)]"
SelectionMode="Single" Grid.ColumnSpan="4">
these are the column Definitions:
<DataGridTextColumn Binding="{Binding ID}" Header="PersonalNumber"/>
<DataGridTextColumn Binding="{Binding FullName}" Header="FullName"/>
To solve this, I tried something like this, but doesn't work:
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding Passed}" Value="false">
<Setter Property="Background" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding Passed}" Value="true">
<Setter Property="Background" Value="Black"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
What can I do?
Your DataGrid is setting RowBackground="White" which is overriding the RowStyle, remove that setting and the Style will behave as you're expecting it to.

Unable to disable rows in WPF DataGrid using Style trigger

I want to disable rows based on a property "IsEditable".
This is my RowStyle
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="IsEnabled" Value="True"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsEditable}" Value="False">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
I have implemented INotifyPropertyChanged.
I'm able to disable the entire grid using the same property but I'm unable to disable rows.
"IsEditable" property is defined in my ViewModel. My datacontext is also the ViewModel.
This is my grid code
<DataGrid RowHeaderWidth="0" MouseDoubleClick="listViewItem_MouseDoubleClick"
AutoGenerateColumns="False" IsReadOnly="True" SelectionMode="Extended" SelectionUnit="FullRow"
BorderBrush="#FF898C95" ItemsSource="{Binding EqpGrpList}" CanUserSortColumns="False" HorizontalGridLinesBrush="#FFDEDBDB"
VerticalGridLinesBrush="#FFDEDBDB" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Margin="0,-5,0,0" Background="White" CellStyle="{StaticResource DataGridCellStyle}">
<DataGrid.Columns...>
</DataGrid>
I have included the row style in UserControl.Resources.
Bind your property into DataGrid ItemCollection Class.
xaml....
<Style x:Key="DataGridCellStyle" TargetType="DataGridRow">
<Setter Property="IsEnabled" Value="True"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsEnable}" Value="False">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
<DataGrid RowHeaderWidth="0"
AutoGenerateColumns="False" IsReadOnly="True" SelectionMode="Extended" SelectionUnit="FullRow"
BorderBrush="#FF898C95" ItemsSource="{Binding GridData}" CanUserSortColumns="False" HorizontalGridLinesBrush="#FFDEDBDB"
VerticalGridLinesBrush="#FFDEDBDB" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Margin="0,-5,0,0" Background="White" RowStyle="{StaticResource DataGridCellStyle}">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridTextColumn Header="City" Binding="{Binding City}"/>
</DataGrid.Columns>
</DataGrid>
Code...
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
for(int i=0;i<10;i++)
{
GridData.Add(new UserData { Name = "NAME " + i, City = "CITY " + i });
}
}
private ObservableCollection<UserData> _gridData=new ObservableCollection<UserData>();
public ObservableCollection<UserData> GridData
{
get { return _gridData; }
set { _gridData = value; }
}
class....
public class UserData
{
public string Name { get; set; }
public string City { get; set; }
private bool _isEnable = false;
public bool IsEnable
{
get { return _isEnable; }
set { _isEnable = value; }
}
}

Disable specific DataGridCheckBoxColumn cells via ViewModel

My MVVM project has this ViewModel:
class ListViewModel : ViewModelBase {
public ObservableCollection<ListItemviewModel> Items { ... }
}
class ListItemViewModel : ViewModelBase {
public String Name { ... }
public Boolean IsChecked { ... }
public Boolean IsEnabled { ... }
}
Writing the XAML seemed straightforward:
<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridCheckBoxColumn Header="Is checked" Binding="{Binding IsChecked}" />
</DataGrid>
However, how can I get it so when a ListItemViewModel's IsEnabled property is false the DataGridCheckBoxColumn's cell in that row is disabled?
I tried setting IsReadOnly={Binding IsDisabled} (and adding an IsDisabled property to ListItemViewModel, however to no avail) - and I recognise that would disable/enable the whole column, not individual cells.
I also tried these instructions ( How to disable a cell in a DataGrid while binding to an ObservableCollection ):
<DataGridCheckBoxColumn Header="Is checked" Binding="{Binding IsChecked}" />
<DataGridCheckBoxColumn.CellStyle>
<Style TargetType="DataGridCell">
<Style.Triggers>
<DataTrigger Binding="{Binding IsEnabled}" Value="False">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridCheckBoxColumn.CellStyle>
However this has no effect and no binding errors are displayed in the Output window.
It turns out the question I linked to ( How to disable a cell in a DataGrid while binding to an ObservableCollection ) was almost-right, but the XAML was somewhat cargo-cultish. The correct XAML is merely:
<DataGridCheckBoxColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="IsEnabled" Value="{Binding IsEnabled}" />
</Style>
</DataGridCheckBoxColumn.CellStyle>
Sorted!

DataGrid don't update after changing the ItemsSource

I have a data grid which I binded to a ObservableCollection object.
On an event, I clear the ObservableCollection and add new items to it.
When finished, I try to update the DataGrid, but it still shows the old rows.
What am I doing wrong?
This is my XAML:
<DataGrid
ItemsSource="{Binding }"
AutoGenerateColumns="False"
Name="dgvCurrentFaults"
TabIndex="0"
Background="Transparent"
RowBackground="#B4CDCD"
Foreground="#314E54" >
<DataGrid.Columns>
<DataGridTemplateColumn Header="Icon" Width="70" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding Icon}" Width="20" Height="20"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridHyperlinkColumn Header="Display" Binding="{Binding Display}" ContentBinding="{Binding Display}" IsReadOnly="True">
<DataGridHyperlinkColumn.ElementStyle>
<Style>
<EventSetter Event="Hyperlink.Click" Handler="dgvCurrentFaults_CellContentClick"/>
</Style>
</DataGridHyperlinkColumn.ElementStyle>
</DataGridHyperlinkColumn>
<DataGridTextColumn Header="Fault Name" Binding="{Binding Falut_Name}" Width="150" IsReadOnly="True">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Description" Binding="{Binding Fault_Description}" Width="240" IsReadOnly="True">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Action Required" Binding="{Binding ActionRequired}" Width="200" IsReadOnly="True">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="ID Fault" Binding="{Binding IDFault}" Visibility="Hidden" IsReadOnly="True"/>
</DataGrid.Columns>
</DataGrid>
and this is my code
public ObservableCollection<FaultsInfo> infoFaultList { get; set; }
private void UpdateTable()
{
infoFaultList.Clear();
infoFaultList.Add(new infoFault(1));
infoFaultList.Add(new infoFault(2));
dgvCurrentFaults.ItemsSource = null;
dgvCurrentFaults.ItemsSource = infoFaultList;
dgvCurrentFaults.UpdateLayout();
dgvCurrentFaults.Items.Refresh();
}
Edit:
After having many more looks on the subject, I see that the first time I update the DataGrid is on the Loaded event of the UserControl. In this case, the DataGrid update fine.
Later, the DataGrid updates on an event that is launched by some communication. In that case, it dose not udate.
I thought that maybe the problem is that I try to update it from another thread, although I use Invoke.
I the source property is an observable collection, you doesn't need to set it again. You doesn't need this code:
dgvCurrentFaults.ItemsSource = null;
dgvCurrentFaults.ItemsSource = infoFaultList;
Also, the code above also doesn't works because the class must implement the INotifyPropertyChanged interface:
ObservableCollection<FaultsInfo> _infoFaultList;
public ObservableCollection<FaultsInfo> infoFaultList
{
get
{
return _infoFaultList;
}
set
{
_infoFaultList = value;
NotifyPropertyChanged("infoFaultList");
}
}
Also you need to check if the binding is working. If your xaml code is right, then the data grid's data context should be the collection itself before the binding working like you are using it: ItemsSource="{Binding }". If the collection is not the data context, then you should fix the binding and make some one like this ItemsSource="{Binding InfoFaultList}".
Hope this helps.
public ObservableCollection<FaultsInfo> infoFaultList { get; set; }
public MainWindow2()
{
InitializeComponent();
infoFaultList = new ObservableCollection<FaultsInfo>();
infoFaultList.Add(new FaultsInfo(5));
infoFaultList.Add(new FaultsInfo(6));
dgvCurrentFaults.ItemsSource = infoFaultList;
dgvCurrentFaults.UpdateLayout();
}
private void UpdateTable()
{
infoFaultList.Clear();
infoFaultList.Add(new FaultsInfo(1));
infoFaultList.Add(new FaultsInfo(2));
dgvCurrentFaults.ItemsSource = null;
dgvCurrentFaults.ItemsSource = infoFaultList;
dgvCurrentFaults.UpdateLayout();
dgvCurrentFaults.Items.Refresh();
}
private void btnName_Click_1(object sender, RoutedEventArgs e)
{
UpdateTable();
}
XAML Code
<StackPanel Orientation="Vertical">
<Grid>
<DataGrid
ItemsSource="{Binding }"
AutoGenerateColumns="False"
Name="dgvCurrentFaults"
TabIndex="0"
Background="Transparent"
RowBackground="#B4CDCD"
Foreground="#314E54" >
<DataGrid.Columns>
<!--<DataGridTemplateColumn Header="Icon" Width="70" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding Icon}" Width="20" Height="20"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>-->
<DataGridHyperlinkColumn Header="Display" Binding="{Binding Display}" ContentBinding="{Binding Display}" IsReadOnly="True">
</DataGridHyperlinkColumn>
<DataGridTextColumn Header="Fault Name" Binding="{Binding Falut_Name}" Width="150" IsReadOnly="True">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Description" Binding="{Binding Fault_Description}" Width="240" IsReadOnly="True">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Action Required" Binding="{Binding ActionRequired}" Width="200" IsReadOnly="True">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="ID Fault" Binding="{Binding IDFault}" Visibility="Hidden" IsReadOnly="True"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
<Button x:Name="btnName" Width="100" Height="100" Content="Click Me" Click="btnName_Click_1"/>
</StackPanel>
Above is my code and Its working fine there is no issue u can follow the provided syntax. During testing above code i came to know that whenever I clear infoFaultList or set it to null we had to reinitialize like this.
infoFaultList=new ObservableCollection<FaultsInfo>();

Weird issues styling a foreground to a DataGrid

Honestly not sure where I am going wrong here, but for whatever reason the foreground style seen below is not being applied to the datagrid. I'm at a loss for this one as I really don't know how I would go about debugging the xaml.
<DataGrid Name="dgProperties" Background="#1E918D8D" SelectionMode="Extended" SelectionUnit="FullRow" ItemsSource="{Binding CurFieldData}" AutoGenerateColumns="False" CanUserReorderColumns="False" CanUserSortColumns="True" IsReadOnly="True">
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}" >
<Setter Property="Foreground" Value="#3535bb" />
<!--<Style.Triggers>
<DataTrigger Binding="{Binding Path=DiffState}" Value="Different">
<Setter Property="Foreground" Value="#3535BB" />
</DataTrigger>
</Style.Triggers>-->
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="Property" FontSize="12" Binding="{Binding Name}" Width="2*" />
<DataGridTextColumn Header="Left Value" FontSize="12" Binding="{Binding LeftValue}" Width="4*" />
<DataGridTextColumn Header="Right Value" FontSize="12" Binding="{Binding RightValue}" Width="4*"/>
</DataGrid.Columns>
</DataGrid>
You can probably tell through the commented out trigger, but I originally planned on recoloring all entries in the grid that are marked as different (an enum in the code behind). However, that wasn't working for me so I wanted to try and see if the style was being set at all, regardless of the trigger.
Does anybody see or know why this style is not being applied?
It seems to be working just fine, I have added my test code below incase it helps
Xaml:
<Window x:Class="WpfApplication7.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" Name="UI">
<DataGrid Name="dgProperties" Background="#1E918D8D" SelectionMode="Extended" SelectionUnit="FullRow" ItemsSource="{Binding Items, ElementName=UI}" AutoGenerateColumns="False" CanUserReorderColumns="False" CanUserSortColumns="True" IsReadOnly="True">
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}" >
<Setter Property="Foreground" Value="Black" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=DiffState}" Value="Different">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="Property" FontSize="12" Binding="{Binding Name}" Width="2*" />
<DataGridTextColumn Header="Left Value" FontSize="12" Binding="{Binding LeftValue}" Width="4*" />
<DataGridTextColumn Header="Right Value" FontSize="12" Binding="{Binding RightValue}" Width="4*"/>
</DataGrid.Columns>
</DataGrid>
</Window>
Code:
public partial class MainWindow : Window
{
private ObservableCollection<GridItem> items = new ObservableCollection<GridItem>();
public MainWindow()
{
InitializeComponent();
for (int i = 0; i < 1000; i++)
{
Items.Add(new GridItem { Name = "StackOverflow" + i, LeftValue = i % 4, RightValue = i % 2 });
}
}
public ObservableCollection<GridItem> Items
{
get { return items; }
set { items = value; }
}
}
public class GridItem
{
public string Name { get; set; }
public int LeftValue { get; set; }
public int RightValue { get; set; }
public DiffState DiffState
{
get
{
if (LeftValue == RightValue)
{
return DiffState.Same;
}
return DiffState.Different;
}
}
}
public enum DiffState
{
Different,
Same
}
Result:
The styling of a DataGridRow can be confounded by settings that occur higher up the object graph, especially the Alternating Row Background. As shown in sa_ddam213's answer, the Xaml works when nothing else is declared. So purely as a diagnostic accelerator, add these four lines to the DataGrid declaration...
RowBackground="Transparent"
Background="Transparent"
AlternatingRowBackground="Transparent"
Foreground="Transparent"
And then inspect your foreground property and its trigger. You can then mutate those four properties on the DataGrid to identify the confounding influence (and then remove it).

Resources