How to bind ListViewItem's IsSelected to property in Style - c#

I'm trying to highlight a row in my ListView based on a property of class. The ItemSource is set to an ObservableCollection<FileInformation> the property I need to determine whether the row should be highlighted is contained in the FileInformation class - bool IsPlaying.
This is my ListView xaml:
<ListView Name="lvListView" Margin="0,0,0,35" AllowDrop="True" ItemsSource="{Binding FilesCollection}">
<ia:Interaction.Triggers>
<ia:EventTrigger EventName="Drop">
<cmd:EventToCommand Command="{Binding ListViewFileDrop}" PassEventArgsToCommand="True"/>
</ia:EventTrigger>
<ia:EventTrigger EventName="MouseDoubleClick">
<cmd:EventToCommand Command="{Binding ListViewDoubleClickCommand}" PassEventArgsToCommand="True"/>
</ia:EventTrigger>
</ia:Interaction.Triggers>
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove" Command="{Binding RemoveCommand}" CommandParameter="{x:Reference Name=lvListView}"/>
<MenuItem Header="Add File" Command="{Binding BrowseCommand}"/>
<MenuItem Header="Clear All" Command="{Binding ClearAllCommand}"/>
</ContextMenu>
</ListView.ContextMenu>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="IsSelected" Value="{Binding RelativeSource={RelativeSource Self}, Path=IsPlaying, Converter={converters:TestConverter}}"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Yellow"/>
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="Title" Width="350" DisplayMemberBinding="{Binding Title}"/>
<GridViewColumn Header="Time" Width="114"
DisplayMemberBinding="{Binding Time, Converter={converters:TimeSpanFormatConverter}}"/>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
This is the part I'm struggling with:
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="IsSelected" Value="{Binding RelativeSource={RelativeSource Self}, Path=IsPlaying, Converter={converters:TestConverter}}"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Yellow"/>
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
Right now in order to highlight the row I need to click on it, I want it to be highlighted in yellow only when the property IsPlaying is set to true.
This is the property declaration:
public static readonly DependencyProperty IsPlayingProperty =
DependencyProperty.Register(nameof(IsPlaying), typeof(bool), typeof(FileInformation),
new PropertyMetadata(null));
private bool _isPlaying = false;
public bool IsPlaying
{
get => (bool)GetValue(IsPlayingProperty);
set
{
SetValue(IsPlayingProperty, value);
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}

See if this helps at all. Take a look at the converters XAML (both in the binding and in Window.Resources).
I added a read only datagrid to see the raw data and a button to toggle (true/false) a specific FileInformation.IsPlaying.
MainWindow.xaml
<Window x:Class="WpfApp13.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:WpfApp13"
xmlns:converters="clr-namespace:WpfApp13"
mc:Ignorable="d"
Title="MainWindow" Height="600" Width="525">
<Window.Resources>
<converters:TestConverter x:Key="TestConverter" />
<converters:TimeSpanFormatConverter x:Key="TimeSpanFormatConverter" />
</Window.Resources>
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- Since it looks like you want to play audio, I set SelectionMode to Single. Probably don't want to play multiple at once -->
<ListView Name="lvListView" Margin="0,0,0,35" AllowDrop="True" ItemsSource="{Binding FilesCollection}" SelectionMode="Single">
<!--<ia:Interaction.Triggers>
<ia:EventTrigger EventName="Drop">
<cmd:EventToCommand Command="{Binding ListViewFileDrop}" PassEventArgsToCommand="True"/>
</ia:EventTrigger>
<ia:EventTrigger EventName="MouseDoubleClick">
<cmd:EventToCommand Command="{Binding ListViewDoubleClickCommand}" PassEventArgsToCommand="True"/>
</ia:EventTrigger>
</ia:Interaction.Triggers>-->
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove" Command="{Binding RemoveCommand}" CommandParameter="{x:Reference Name=lvListView}"/>
<MenuItem Header="Add File" Command="{Binding BrowseCommand}"/>
<MenuItem Header="Clear All" Command="{Binding ClearAllCommand}"/>
</ContextMenu>
</ListView.ContextMenu>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Background" Value="Beige"/>
<!-- The converter, TestConverter, isn't even needed but I'll leave it since you might be doing something else with it -->
<Setter Property="IsSelected" Value="{Binding Path=IsPlaying, Converter={StaticResource TestConverter}}"/>
<Style.Triggers>
<!--<Trigger Property="IsSelected" Value="True">-->
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Yellow"/>
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="Title" Width="350" DisplayMemberBinding="{Binding Title}"/>
<GridViewColumn Header="Time" Width="114"
DisplayMemberBinding="{Binding Time, Converter={StaticResource TimeSpanFormatConverter}}"/>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
<DataGrid Grid.Row="1" AutoGenerateColumns="False" ItemsSource="{Binding FilesCollection}" IsReadOnly="True">
<DataGrid.Columns>
<DataGridCheckBoxColumn Binding="{Binding IsPlaying}" Header="IsPlaying" />
<DataGridTextColumn Binding="{Binding Title}" Header="Title" />
<DataGridTextColumn Binding="{Binding Time}" Header="Time" />
</DataGrid.Columns>
</DataGrid>
<Button Content="Toggle File 5 IsPlaying" Grid.Row="2" Margin="10" Click="Button_Click" />
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace WpfApp13
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
((MainWindowViewModel)DataContext).PlayToggle(4); // I'm too lazy to use an ICommand...
}
}
public class MainWindowViewModel : DependencyObject
{
public ObservableCollection<FileInformation> FilesCollection
{
get { return (ObservableCollection<FileInformation>)GetValue(FilesCollectionProperty); }
set { SetValue(FilesCollectionProperty, value); }
}
public static readonly DependencyProperty FilesCollectionProperty = DependencyProperty.Register("FilesCollection", typeof(ObservableCollection<FileInformation>), typeof(MainWindowViewModel), new PropertyMetadata(null));
public MainWindowViewModel()
{
FilesCollection = new ObservableCollection<FileInformation>() {
new FileInformation() { Title = "File 1", IsPlaying = false, Time = new TimeSpan(0, 5, 1) },
new FileInformation() { Title = "File 2", IsPlaying = false, Time = new TimeSpan(0, 5, 2) },
new FileInformation() { Title = "File 3", IsPlaying = false, Time = new TimeSpan(0, 5, 3) },
new FileInformation() { Title = "File 4", IsPlaying = false, Time = new TimeSpan(0, 5, 4) },
new FileInformation() { Title = "File 5", IsPlaying = false, Time = new TimeSpan(0, 5, 5) },
new FileInformation() { Title = "File 6", IsPlaying = false, Time = new TimeSpan(0, 5, 6) },
new FileInformation() { Title = "File 7", IsPlaying = false, Time = new TimeSpan(0, 5, 7) },
new FileInformation() { Title = "File 8", IsPlaying = false, Time = new TimeSpan(0, 5, 8) },
new FileInformation() { Title = "File 9", IsPlaying = false, Time = new TimeSpan(0, 5, 9) },
};
}
public void PlayToggle(int idx)
{
FilesCollection[idx].IsPlaying = !FilesCollection[idx].IsPlaying;
}
}
public class FileInformation : DependencyObject
{
public static readonly DependencyProperty IsPlayingProperty = DependencyProperty.Register(nameof(IsPlaying), typeof(bool), typeof(FileInformation), new PropertyMetadata(null));
//private bool _isPlaying = false;
public bool IsPlaying
{
get => (bool)GetValue(IsPlayingProperty);
set
{
SetValue(IsPlayingProperty, value);
OnPropertyChanged();
}
}
public static readonly DependencyProperty TitleProperty = DependencyProperty.Register(nameof(Title), typeof(string), typeof(FileInformation), new PropertyMetadata(null));
public string Title
{
get => (string)GetValue(TitleProperty);
set
{
SetValue(TitleProperty, value);
OnPropertyChanged();
}
}
public TimeSpan Time
{
get { return (TimeSpan)GetValue(TimeProperty); }
set { SetValue(TimeProperty, value); }
}
public static readonly DependencyProperty TimeProperty = DependencyProperty.Register("Time", typeof(TimeSpan), typeof(FileInformation), new PropertyMetadata(null));
public event PropertyChangedEventHandler PropertyChanged;
//[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
public class TestConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// value = IsPlaying
if (value is bool b)
{
return b;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value; // Needed because IsPlaying is twoway bound to IsSelected.
}
}
public class TimeSpanFormatConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// value = Time
if (value is TimeSpan ts)
{
return $"{ts.Minutes} minutes {ts.Seconds} seconds";
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
If you do not want IsSelected tied to IsPlaying but still want IsPlaying to trigger the yellow background then try this:
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Style.Triggers>
<DataTrigger Binding="{Binding IsPlaying}" Value="True">
<Setter Property="Background" Value="Yellow" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>

Related

WPF Dependency Property Not Binding / PropertyChangedCallback Not Getting Called

My project consists of a Treeview with a Datagrid as a child. The parent in the Treeview has a checkbox, and every row in the datagrid also has a check box. My goal is to bind the checkboxes together, so that when the parent checkbox is checked, all of the datagrid rows are checked as well. When the datagrid rows are checked I would like the parent checkbox to be updated based of the datagrid row checked values.
Parent checkbox IsChecked = true when all of the datagrid row checkboxes are true, null if only some are, and false if no rows are checked.
The problem I'm facing is that my dependency properties aren't updating and the PropertyChangedCallback is not getting called.
This is my Checkbox_Helper class in which the IsChecked properties of both the parent and the datagrid row checkboxes are bound to.
public class CheckBox_Helper : DependencyObject
{
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.RegisterAttached("IsChecked", typeof(bool?), typeof(CheckBox_Helper),
new PropertyMetadata(false, new PropertyChangedCallback(OnIsCheckedPropertyChanged)));
private static void OnIsCheckedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is Parent && ((bool?)e.NewValue).HasValue)
{
foreach (Child s in (d as Parent).SheetList[0].SheetChildList)
{
if (s.IsSizeCompatible)
{
SetIsChecked(s, (bool?)e.NewValue);
}
}
}
if (d is Child)
{
AddToCheckedElements(d as Child);
int _checked = (d.GetValue(ParentProperty) as Parent).SheetList[0].SheetChildList.Where(x => GetIsChecked(x) == true).Count();
int _unchecked = (d.GetValue(ParentProperty) as Parent).SheetList[0].SheetChildList.Where(x => GetIsChecked(x) == false).Count();
int _total = (d.GetValue(ParentProperty) as Parent).SheetList[0].SheetChildList.Count();
if ((d as Child).MegaParent.SelectedRowList != null && (d as Child).MegaParent.SelectedRowList.Count > 0)
{
(d as Child).MegaParent.SelectedRowList.Where(i => GetIsChecked(i) != GetIsChecked(d as Child)).ToList().ForEach(i => CheckBox_Helper.SetIsChecked(i, GetIsChecked(d as Child)));
}
if (_checked == 0)
{
(d.GetValue(ParentProperty) as Parent).HasCheckedItems = false;
}
else
{
(d.GetValue(ParentProperty) as Parent).HasCheckedItems = true;
}
(d.GetValue(ParentProperty) as Parent).CheckedRatio = $"{_checked}/{_total}";
if (_unchecked > 0 && _checked > 0)
{
CheckBox_Helper.SetIsChecked((d as Child).GetValue(CheckBox_Helper.ParentProperty) as DependencyObject, null);
return;
}
if (_checked > 0)
{
CheckBox_Helper.SetIsChecked((d as Child).GetValue(CheckBox_Helper.ParentProperty) as DependencyObject, true);
return;
}
CheckBox_Helper.SetIsChecked((d as Child).GetValue(CheckBox_Helper.ParentProperty) as DependencyObject, false);
}
}
public static readonly DependencyProperty ParentProperty = DependencyProperty.RegisterAttached("Parent", typeof(Parent), typeof(CheckBox_Helper));
public static void SetIsChecked(DependencyObject element, bool? IsChecked)
{
element.SetValue(CheckBox_Helper.IsCheckedProperty, IsChecked);
}
public static bool? GetIsChecked(DependencyObject element)
{
return (bool?)element.GetValue(CheckBox_Helper.IsCheckedProperty);
}
// Set and Get Parent are unused since they are hardcoded categories
public static void SetParent(DependencyObject element, object Parent)
{
element.SetValue(CheckBox_Helper.ParentProperty, Parent);
}
public static object GetParent(DependencyObject element)
{
return (object)element.GetValue(CheckBox_Helper.ParentProperty);
}
private static void AddToCheckedElements(Child child)
{
ObservableCollection<Child> tempList = child.MegaParent.CheckedElements;
if (!child.MegaParent.CheckedElements.Contains(child) && GetIsChecked(child) == true)
{
tempList.Add(child);
child.MegaParent.CheckedElements = tempList;
}
else if (child.MegaParent.CheckedElements.Contains(child) && GetIsChecked(child) == false)
{
tempList.Remove(child);
child.MegaParent.CheckedElements = tempList;
}
}
}
This is the code for the Parent class:
public class Parent : DependencyObject
{
public string Name { get; set; }
public ObservableCollection<ChildList> SheetList { get; set; }
public static readonly DependencyProperty HasCheckedItemsProperty =
DependencyProperty.Register("HasCheckedItems", typeof(bool), typeof(Parent), new UIPropertyMetadata(false));
public bool HasCheckedItems
{
get { return (bool)GetValue(HasCheckedItemsProperty); }
set { SetValue(HasCheckedItemsProperty, value); }
}
public static readonly DependencyProperty CheckedRatioProperty =
DependencyProperty.Register("CheckedRatio", typeof(string), typeof(Parent), new UIPropertyMetadata($"0/0"));
public string CheckedRatio
{
get { return (string)GetValue(CheckedRatioProperty); }
set { SetValue(CheckedRatioProperty, value); }
}
public Parent(string name)
{
Name = name;
SheetList = new ObservableCollection<ChildList>();
}
}
This is the code for the Child class:
(Note: The MegaParent property is the viewmodel)
public class Child : DependencyObject
{
public ViewSheet Sheet { get; set; }
public string Name { get; set; }
public string Number { get; set; }
private string _paperSize = "";
public string PaperSize
{
get { return _paperSize; }
set { _paperSize = value; }
}
public static readonly DependencyProperty IsSizeCompatibleProperty =
DependencyProperty.Register("IsSizeCompatible", typeof(bool), typeof(Child), new UIPropertyMetadata(true));
public bool IsSizeCompatible
{
get { return (bool)GetValue(IsSizeCompatibleProperty); }
set { SetValue(IsSizeCompatibleProperty, value); }
}
public PrintSheets_ViewModel MegaParent { get; set; }
public static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.Register("IsSelected", typeof(bool), typeof(Child), new UIPropertyMetadata(false));
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set { SetValue(IsSelectedProperty, value); AddToSelectedList(); }
}
private void AddToSelectedList()
{
ObservableCollection<Child> tempList = MegaParent.SelectedRowList;
if (!MegaParent.SelectedRowList.Contains(this) && (bool)GetValue(IsSelectedProperty) == true)
{
tempList.Add(this);
MegaParent.SelectedRowList = tempList;
}
else if (MegaParent.SelectedRowList.Contains(this) && (bool)GetValue(IsSelectedProperty) == false)
{
tempList.Remove(this);
MegaParent.SelectedRowList = tempList;
}
}
public Child(string name, string number, ViewSheet sheet, string paperSize, bool isSizeCompatible, PrintSheets_ViewModel megaParent)
{
Name = name;
Number = number;
Sheet = sheet;
PaperSize = paperSize;
IsSizeCompatible = isSizeCompatible;
MegaParent = megaParent;
}
}
This is the XAML code for the Treeview:
<Border Grid.Row="1" Grid.RowSpan="4" Grid.Column="3" Grid.ColumnSpan="3" Margin="10,10,10,10"
BorderBrush="Black" BorderThickness="1">
<ScrollViewer x:Name="treeScroller" PreviewMouseWheel="treeScroller_PreviewMouseWheel" VerticalScrollBarVisibility="Auto">
<TreeView Background="LightGray" ItemsSource="{Binding tv_model.ParentItems}" x:Name="sheetTree"
ScrollViewer.CanContentScroll="False">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type m:Parent}" ItemsSource="{Binding SheetList}">
<Border BorderBrush="DarkGray" CornerRadius="5" Padding="5,0,0,0" Height="40"
Width="{Binding Path=ActualWidth, ElementName=sheetTree, Converter={StaticResource minusFortyFiveConverter}}">
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding HasCheckedItems}" Value="True">
<Setter Property="Background" Value="#b5ffc0"/>
<Setter Property="BorderThickness" Value="3"/>
</DataTrigger>
<DataTrigger Binding="{Binding HasCheckedItems}" Value="False">
<Setter Property="Background" Value="LightCyan"/>
<Setter Property="BorderThickness" Value="2"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<CheckBox Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Center"
IsChecked="{Binding Path=(h:CheckBox_Helper.IsChecked), Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
Cursor="Hand"/>
<Label Grid.Column="1" Content="{Binding Name}" VerticalAlignment="Center" HorizontalAlignment="Left" FontWeight="SemiBold"/>
<Label Grid.Column="3" VerticalAlignment="Center" HorizontalContentAlignment="Right"
FontWeight="Normal" FontSize="12"
Content="Sheets Checked:"/>
<Label Grid.Column="4" VerticalAlignment="Center" HorizontalAlignment="Right"
Content="{Binding CheckedRatio}">
<Label.Style>
<Style TargetType="Label">
<Style.Triggers>
<DataTrigger Binding="{Binding HasCheckedItems}" Value="True">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="FontSize" Value="14"/>
</DataTrigger>
<DataTrigger Binding="{Binding HasCheckedItems}" Value="False">
<Setter Property="FontWeight" Value="Normal"/>
<Setter Property="FontSize" Value="12"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
</Grid>
</Border>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type m:ChildList}">
<DataGrid x:Name="ChildDataGrid" ItemsSource="{Binding SheetChildList}"
Width="{Binding Path=ActualWidth, ElementName=sheetTree, Converter={StaticResource minusEightyConverter}}"
IsReadOnly="True" AutoGenerateColumns="False" SelectionMode="Extended" CanUserResizeColumns="True"
EnableRowVirtualization="True" CanUserAddRows="False" CanUserDeleteRows="False"
CanUserResizeRows="True" CanUserReorderColumns="False" CanUserSortColumns="True" >
<DataGrid.Resources>
<Style TargetType="DataGrid">
<EventSetter Event="LostFocus" Handler="OnLostFocus_DataGrid"/>
</Style>
<Style TargetType="DataGridRow">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSizeCompatible, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Value="False">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsSizeCompatible, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Value="True">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTemplateColumn Width="20">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Cursor="Hand" IsChecked="{Binding Path=(h:CheckBox_Helper.IsChecked), Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Sheet Number" Binding="{Binding Number}" IsReadOnly="True" Width="Auto"/>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" IsReadOnly="True" Width="*"/>
<DataGridTextColumn Header="Paper Size" Binding="{Binding PaperSize}" IsReadOnly="True" Width="Auto"/>
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</TreeView.Resources>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="Margin" Value="0,3,0,3"/>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
</ScrollViewer>
</Border>
I had the checkboxes working before I added some of the extra methods to the CheckBox_Helper, but I need those methods in order for other controls on my UI to function properly.
I feel like it could be a DataContext issue. I've tried adding RelativeSource to the IsChecked binding with the AncestorType as Datagrid, and I've also tried it as Window.
Still no luck, any help would be greatly appreciated.

how to focus on the keyboard on a textbox in WPF listview after item.refresh

I have a WPF window with a listview with textboxes in it, and I want to enable users to switch between textboxes with the TAB key. I created a function that does that, but every time the textbox loses focus, I refresh the listview and so the listview itself gets focused.
I know the problem occurs because of the refresh event because commenting that part will result in the correct element (the textbox) being focused.
I have tried many alternative solutions but none works (prevent item losing focus when refreshing list view in c#; Focus on TextBox after ListView 'SelectionChanged' Event).
It seems there is something problematic in focusing on an that element after the listview is refreshed.
I have tried remembering the index of the item and then focusing on it, after the TAB key was pressed. tried also remembering the focused control and then focusing on it after the refresh.
private void RTB_Reference_LostFocus(object sender, RoutedEventArgs e)
{
int Index = DetailsList.SelectedIndex;
Index = DetailsList.Items.IndexOf(DetailsList.SelectedItem);
try
{
//Get cell value by using sender Object
string inTime = ((System.Windows.Controls.TextBox)sender).Text;
DetailItem item = (DetailItem)DetailsList.Items[Index];
item.Reference = inTime;
UpdateExplanation(item);
}
catch (Exception)
{
}
}
private void RTB_Detail_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == Key.Tab)
{
e.Handled = true;
//System.Windows.MessageBox.Show("Tab");
int Idx = DetailsList.SelectedIndex;
System.Windows.Controls.ListViewItem lvi = (System.Windows.Controls.ListViewItem)DetailsList.ItemContainerGenerator.ContainerFromItem(DetailsList.SelectedItem);
GUF.FocusItem(DetailsList, Idx, "RTB_Detail");
//IsLastKeyTAB = true;
}
//else
// IsLastKeyTAB = false;
}
private void UpdateExplanation(DetailItem item)
{
item.Explanation = GetExplanation(item.Reference, item.Detail);
IInputElement focusedControl = Keyboard.FocusedElement;
DetailsList.Items.Refresh();
focusedControl.Focus();
RefreshDetailsList(DetailsList, IsEnglish);
}
expected result is to keep focus on that textbox after the refresh. that does not occur...
EDIT 1
This is the listview xaml:
<ListView FlowDirection="RightToLeft" x:Name="DetailsList" VirtualizingStackPanel.IsVirtualizing="False"
HorizontalAlignment="Stretch" VerticalAlignment="Top"
HorizontalContentAlignment="Center" ScrollViewer.VerticalScrollBarVisibility="Visible" Padding="0 0 0 25"
AllowDrop="True"
ItemsSource="{Binding DetailItem}"
Loaded="ListView_Loaded"
Margin="26,157,23,0"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.DropHandler="{Binding}" Height="599">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="Margin" Value="4, 4, 4, 4"/>
<Setter Property="FontWeight" Value="DemiBold"/>
<Setter Property="Height" Value="22"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Border BorderBrush="Transparent" BorderThickness="0" Background="{TemplateBinding Background}">
<GridViewRowPresenter HorizontalAlignment="Stretch" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Width="Auto" Margin="0" Content="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<!-- <Setter Property="Background" Value="#6B54FF"/> -->
<Setter Property="Foreground" Value="#6B57FF"/>
<Setter Property="BorderThickness" Value="2" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="BorderThickness" Value="2" />
<Setter Property="BorderBrush" Value="#6B57FF"/>
<Setter Property="Foreground" Value="#6B57FF" />
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Width="50" Header="סימון" DisplayMemberBinding="{Binding Mark}"/>
<!--
<GridViewColumn Width="30" >
<GridViewColumn.CellTemplate>
<DataTemplate >
<Button Style="{StaticResource PlusButtonStyle}" x:Name="buttonPlusDocument" Click="buttonPlusDocument_Click" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
-->
<GridViewColumn Header="הפניה במסמך" Width="150">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox x:Name="RTB_Reference" BorderBrush="#5f27ff" BorderThickness="1" KeyDown="RTB_Reference_KeyDown" HorizontalAlignment="Stretch" Height="20" Margin="0" Padding="0" FontSize="12" IsEnabled="True"
LostFocus="RTB_Reference_LostFocus" GotFocus="RTB_Reference_GotFocus">
<TextBox.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="2"/>
<Setter Property="BorderBrush" Value="#5f27ff"/>
<Setter Property="BorderThickness" Value="1" />
</Style>
</TextBox.Resources>
</TextBox>
<!--DataContext="{Binding SelectedItem, ElementName=ListViewAppendixNameList, Mode=TwoWay}"-->
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="פרט" Width="150">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox x:Name="RTB_Detail" BorderBrush="#5f27ff" BorderThickness="1" HorizontalAlignment="Stretch" Height="20" Margin="0" Padding="0" FontSize="12" IsEnabled="True"
KeyDown="RTB_Detail_KeyDown" LostFocus="RTB_Detail_LostFocus" GotFocus="RTB_Detail_GotFocus">
<TextBox.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="2"/>
<Setter Property="BorderBrush" Value="#5f27ff"/>
<Setter Property="BorderThickness" Value="1" />
</Style>
</TextBox.Resources>
</TextBox>
<!--DataContext="{Binding SelectedItem, ElementName=ListViewAppendixNameList, Mode=TwoWay}"-->
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="הסבר" Width="350" DisplayMemberBinding="{Binding Explanation}"/>
<GridViewColumn Width="30" >
<GridViewColumn.CellTemplate>
<DataTemplate >
<Button Style="{StaticResource DeleteButtonStyle}" x:Name="BT_DeleteDetail" Click="BT_DeleteDetail_Click" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
This is the details class:
public class DetailItem
{
public string Mark { get; set; }
public string Reference { get; set; }
public string Detail { get; set; }
public string Explanation { get; set; }
}
I refresh the listview so that the Explanation text will be updated.
I don't went through your whole code but only those parts that are relevant to your question. To simplify things, I will use the attached property TabNavigation and the data binding feature. The binding will automatically update the control values so that the refreshing of the ListView and the focus handling becomes redundant.
Remark: To shorten the XAML code I will only show the relevant code portions.
Tab navigation
Set the attached property KeyboardNavigation.TabNavigation on the ListView to Continue so that the focus will loop through the list:
<ListView x:Name="DetailsList"
KeyboardNavigation.TabNavigation="Continue"
...
</ListView>
You can control which elements can receive focus on Tab key pressed by setting the property IsTabStop on the elements. The default value is True. So set it to False on those elements you wish to exclude. It's useful to disable Tab focus on the ListViewItem itself so that the Tab key is only effective for the containing controls:
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="IsTabStop"
Value="False" />
...
</Style>
</ListView.ItemContainerStyle>
If you wish to exclude the Button as well, so that you only jump between the TextBox elements, set the Button's IsTabStopproperty to False as well:
<GridViewColumn Width="30">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button x:Name="BT_DeleteDetail"
IsTabStop="False"
...
/>
...
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
If you also wish to change the order the elements will receive focus on Tab key pressed, you can do it by setting the element's TabIndex property. A control with a lower tab index receives focus before a control with a higher index.
Binding the TextBox
To enable binding to a data model it is required that the data model (view model) implements INotifyPropertxChanged:
public class DetailItem : INotifyPropertyChanged
{
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
private string mark;
public string Mark
{
get => this.mark;
set
{
if (value == this.mark) return;
this.mark = value;
OnPropertyChanged();
}
}
private string reference;
public string Reference
{
get => this.reference;
set
{
if (value == this.reference) return;
this.reference = value;
OnPropertyChanged();
}
}
private string detail;
public string Detail
{
get => this.detail;
set
{
if (value == this.detail) return;
this.detail = value;
OnPropertyChanged();
}
}
private string explanation;
public string Explanation
{
get => this.explanation;
set
{
if (value == this.explanation) return;
this.explanation = value;
OnPropertyChanged();
}
}
}
In order access the view model from your DataTemplate set its DataType property to the type of the view model (DetailItem). Remember to always set the DataType of a DataTemplate. Then add the Binding to the TextBox. Note that the Mode (direction) of the binding is set to OneWayToSource. This limits the binding to set data from TextBoxto DetailItem only:
<GridViewColumn.CellTemplate>
<DataTemplate DataType="viewModels:DetailItem">
<TextBox x:Name="RTB_Reference"
Text="{Binding Reference, Mode=OneWayToSource}"
...
</TextBox>
...
</DataTemplate>
</GridViewColumn.CellTemplate>
<GridViewColumn.CellTemplate>
<DataTemplate DataType="viewModels:DetailItem">
<TextBox x:Name="RTB_Detail"
Text="{Binding Detail, Mode=OneWayToSource}"
...
</TextBox>
...
</DataTemplate>
</GridViewColumn.CellTemplate>
The final step is to update the DetailItem.Explanation property. To do this, we move the UpdateExplanation() method into the view model DetailItem. Since we use binding we can now get rid of all the ListView refresh and focus logic inside, so the method becomes smaller. Note that since we moved the method into the DetailItem class, we can also remove the parameter of the method:
private void UpdateExplanation()
{
this.Explanation = GetExplanation(this.Reference, this.Detail);
}
The UpdateExplanation() method is invoked directly from the setter of the Reference and Detail property, so that everytime these properties are changed the Explanation value will be updated. With the updated Reference and Detail setter the final version of the DetailItem class will then look as followed:
public class DetailItem : INotifyPropertyChanged
{
private void UpdateExplanation()
{
this.Explanation = GetExplanation(this.Reference, this.Detail);
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
private string mark;
public string Mark
{
get => this.mark;
set
{
if (value == this.mark) return;
this.mark = value;
OnPropertyChanged();
}
}
private string reference;
public string Reference
{
get => this.reference;
set
{
if (value == this.reference) return;
this.reference = value;
OnPropertyChanged();
UpdateExplanation();
}
}
private string detail;
public string Detail
{
get => this.detail;
set
{
if (value == this.detail) return;
this.detail = value;
OnPropertyChanged();
UpdateExplanation();
}
}
private string explanation;
public string Explanation
{
get => this.explanation;
set
{
if (value == this.explanation) return;
this.explanation = value;
OnPropertyChanged();
}
}
}
Try Below Code. Enjoy !!!
<Grid>
<ListView Margin="45.5,47.5,41.167,0" Name="lvtestListview" Height="188.333" VerticalAlignment="Top">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<!--<Setter Property="IsSelected" Value="{Binding IsGroovy}"/>-->
<Style.Triggers>
<Trigger Property="IsSelected" Value="true" >
<Setter Property="Foreground" Value="Blue" />
<Setter Property="Background" Value="LightGreen" />
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Header="Item Code" Width="100">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Border BorderBrush="Gray" BorderThickness=".5" Margin="-6,-3">
<TextBlock Text="{Binding ItemCode}" Margin="6,3"/>
</Border>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Item Name" Width="100">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Border BorderBrush="Gray" BorderThickness=".5" Margin="-6,-3">
<TextBox Loaded="txtBarcode_Loaded" Text="{Binding ItemName}" Name="txtBarcode" Focusable="{Binding IsFocused}" Cursor="IBeam" />
</Border>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Next Item" Width="100">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Border BorderBrush="Gray" BorderThickness=".5" Margin="-6,-3">
<TextBox TabIndex="3" Name="txtgvPL1" KeyDown="txtgvPL1_KeyDown" Text="{Binding PL1}" Cursor="IBeam"/>
</Border>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
code behind
public class ClassA : INotifyPropertyChanged
{
public string ItemCode { get; set; }
public string ItemName { get; set; }
private bool isFocused;
public event PropertyChangedEventHandler PropertyChanged;
public bool IsFocused
{
get
{
return isFocused;
}
set
{
if (value != isFocused)
{
isFocused = value;
RaisePropertyChanged("IsFocused");
}
}
}
private void RaisePropertyChanged(string propName)
{
PropertyChangedEventHandler eh = PropertyChanged;
if (eh != null)
{
eh(this, new PropertyChangedEventArgs(propName));
}
}
}
public partial class MainWindow : Window
{
List<ClassA> lstClassA = new List<ClassA>();
public MainWindow()
{
InitializeComponent();
BindGrid();
}
private void BindGrid()
{
ClassA ca = new ClassA();
ca.ItemCode = "A";
ca.IsFocused = true;
lstClassA.Add(ca);
lvtestListview.ItemsSource = lstClassA;
}
private void txtBarcode_Loaded(object sender, RoutedEventArgs e)
{
Keyboard.Focus(((TextBox)sender));
}
private void txtgvPL1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter )
{
ClassA ca = new ClassA();
ca.ItemCode = "B";
ca.IsFocused = true;
lstClassA.Add(ca);
foreach (ClassA a in lstClassA)
{
if (a.ItemCode == "A")
{
a.IsFocused = false;
}
}
lvtestListview.ItemsSource = lstClassA;
lvtestListview.Items.Refresh();
}
}
}

Change button tooltip with style - WPF

I have a DataGrid loaded with a list of objects that have a property bool IsAutomaticSell. I need that when changing the value, the tooltip of the button of that row is updated. I have the following code but it does not work. Thx
View.xaml
<DataGridTextColumn Header="Code"
Binding="{Binding Code}" />
<DataGridTemplateColumn Header="Actions"
Width="150">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center">
<Button Command="{Binding DataContext.AutomaticSellCommand,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
CommandParameter="{Binding}"
Padding="10"
Margin="0,2,2,2">
<iconPacks:PackIconModern Kind="CurrencyDollar" />
<Button.Style>
<Style TargetType="{x:Type Button}"
BasedOn="{StaticResource AccentedSquareButtonStyle}">
<Setter Property="ToolTip"
Value="DEFAULT_TOOLTIP" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsAutomaticSell, UpdateSourceTrigger=PropertyChanged}"
Value="True">
<Setter Property="ToolTip"
Value="NEW_TOOLTIP" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
ViewModel.cs
public ICommand AutomaticSellCommand => _automaticSellCommand ??
(_automaticSellCommand = new RelayCommand<OrderStatusDataWrapper>(AutomaticSell));
private static void AutomaticSell(OrderStatusDataWrapper orderStatusData)
{
orderStatusData.IsAutomaticSell = !orderStatusData.IsAutomaticSell;
}
Bind ToolTip.
XAML:
<Button ToolTip={Binding ToolTip}" ... />
ViewModel:
public string ToolTip => (IsAutomaticSell) ? "DEFAULT_TOOLTIP" : "NEW_TOOLTIP";
You need to implement a ValueConverter in order to get this to work.
A ValueConverter will allow you to handle what to display based on your boolean value.
MSDN
public class BoolToContentConverter : IValueConverter
{
public BoolToContentConverter()
{
TrueContent = "True Tool Tip";
FalseContent = "False Tool Tip";
NullContent = "No Value";
}
public object TrueContent { get; set; }
public object FalseContent { get; set; }
public object NullContent { get; set; }
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
if (value == null)
return NullContent;
bool boolValue = true;
bool isBool = true;
try
{
boolValue = (bool) value;
}
catch
{
isBool = false;
}
if (!isBool)
return NullContent;
return boolValue ? TrueContent : FalseContent;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
In your XAML you need to implement that converter.
<Window x:Class="Example.MainWindow"
...
xmlns:l="clr-Example"
...>
<Window.Resources>
<l:BoolToContentConverter x:Key="converter" />
</Window.Resources>
...
<DataGridTextColumn Header="Code"
Binding="{Binding Code}" />
<DataGridTemplateColumn Header="Actions"
Width="150">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center">
<Button Command="{Binding DataContext.AutomaticSellCommand,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
CommandParameter="{Binding}"
Padding="10"
Margin="0,2,2,2">
<iconPacks:PackIconModern Kind="CurrencyDollar" />
<Button.Style>
<Style TargetType="{x:Type Button}"
BasedOn="{StaticResource AccentedSquareButtonStyle}">
<Setter Property="ToolTip"
Value="DEFAULT_TOOLTIP" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsAutomaticSell, UpdateSourceTrigger=PropertyChanged}"
Value="True">
<Setter Property="ToolTip"
Value="{Binding IsAutomaticSell, Converter={StaticResource converter}}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
....
</Window>
Hope this answers your question. Cheers!

CanUserAddRows New Row not saving in DataGrid

Created DataGrid and set CanUserAddRows="True"
Have a button which saves updates in the cs file:
private void Save_Click(object sender, RoutedEventArgs e)
{
UnitService unitService = new UnitService();
unitService.SaveUpdates(valuationCase);
MainWindow mainWin = new MainWindow();
mainWin.Show();
this.Close();
}
There is also a textbox not in the datagrid on the window which is editable and this is correctly saving edits with the save click button. Just the new rows aren't.
Any ideas??
datagrid definition:
<DataGrid Name="dgCommentsList" AutoGenerateColumns="False" Margin="10,196,9.953,38.204" CanUserAddRows="True" FontSize="18">
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="FontSize" Value="20" />
<Setter Property="FontWeight" Value="bold" />
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.Columns>
<DataGridTemplateColumn Header="Type" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox x:Name="Type" Text="{Binding Type}" >
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding IsReadOnly}" Value="False">
<Setter Property="TextBox.IsReadOnly" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsReadOnly}" Value="True">
<Setter Property="TextBox.IsReadOnly" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid>
I think you need to set the mode of the binding for it to write back to the underlying object.Plus I noticed your DataGrid does not have an ItemsSource. I'm guessing as this was just a snippet that you left it out.
<TextBox x:Name="Type" Text="{Binding Type, Mode=TwoWay}">
You should commit the edit on the row using dataGrid.CommitEdit()
Edit: After diagnosing the issue here goes
You either need to implement INotifyPropertyChanged on your DataContext class (i.e: Viewmodel) like so:
public class ViewModel: INotifyPropertyChanged
{
private string _type;
public string Type
{
get { return _type; }
set
{
_type = value;
OnPropertyChanged("Type");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Or you extend from DependencyObject and use Dependency Properties, like so:
public class ViewModel: DependencyObject
{
public static readonly DependencyProperty TypeProperty = DependencyProperty.Register(
"Type", typeof (string), typeof (ViewModel), new PropertyMetadata(default(string)));
public int Type
{
get { return (int) GetValue(TypeProperty ); }
set { SetValue(TypeProperty , value); }
}
}
Hope it helps ;)

Making a Button Visible in DataGrid

i've created a Datagrid Control in WPF. how can i make the Button visible only for
row that i have select it so that my Button will be shown in the Button-Column-Cell.
XAML:
<DataGridTemplateColumn x:Name="Button-Column" Header="H." Width="50">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=ObjectType}" Value="E">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=ObjectType}" Value="F">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=ObjectType}" Value= "P">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</DataTemplate.Triggers>
<Button Name="btnTable" Visibility="{Binding Path=ObjectType}" Height="20"
Width="25" Click="Button_Table_Click">
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Here is a solution using multibinding :
xaml:
<Window x:Class="WpfApplication8.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:WpfApplication8="clr-namespace:WpfApplication8"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<WpfApplication8:ButtonVisibilityConverter x:Key="buttonVisibilityConverter"/>
</Window.Resources>
<DataGrid x:Name="dataGrid" SelectedItem="{Binding MySelectedItem}" ItemsSource="{Binding MyObjectList}" SelectionMode="Single">
<DataGrid.Columns>
<DataGridTemplateColumn Header="H." Width="50">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button
Height="20"
Width="25">
<Button.Visibility>
<MultiBinding Converter="{StaticResource buttonVisibilityConverter}">
<Binding Path="Parent.MySelectedItem"/>
<Binding></Binding>
</MultiBinding>
</Button.Visibility>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Window>
Code behind :
namespace WpfApplication8
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
public class Toto
{
public object Parent { get; set; }
public string Name { get; set; }
public string Content { get; set; }
public Toto(string name, string content, object parent)
{
this.Name = name; this.Content = content; this.Parent = parent;
}
}
private object _mySelectedItem;
public object MySelectedItem
{
get { return _mySelectedItem; }
set
{
_mySelectedItem = value;
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("MySelectedItem"));
}
}
}
private List<Toto> _myObjectList;
public List<Toto> MyObjectList
{
get { return _myObjectList; }
set { _myObjectList = value; }
}
public MainWindow()
{
this.MyObjectList = new List<Toto>
{
new Toto("toto1", "content toto1", this),
new Toto("toto2", "content toto2", this)
};
InitializeComponent();
this.DataContext = this;
}
}
}
Converter :
class ButtonVisibilityConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (values[0] == values[1])
{
return Visibility.Visible;
}
else
{
return Visibility.Hidden;
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}

Categories

Resources