First Time poster long time reader and new to wpf so please be gentle with me :-)
I am trying to create a list of users with a ListView, I am grouping in a CollectionView with the offline users in red. With a context menu that only shows when clicking on users name.
For code for the contextmenu I got from another post here ContextMenu for ListViewItem only
My issue is that as soon as I add the context menu, the color change no longer triggers,
so I believe I am mixing the methods without understanding how they are working.
<Grid>
<ListView x:Name="lvUsers" ItemsSource="{Binding items}">
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Icon}"/>
<TextBlock Margin="10,0,0,0" Text="{Binding UserName}"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
<ListView.Resources>
<ContextMenu x:Key="ItemContextMenu">
<MenuItem Header="More Info" Click="MenuItemDelete_Click" Command="{Binding Path=DataContext.MoreInfo, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}" Background="WhiteSmoke" />
</ContextMenu>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Foreground" Value = "Red"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Status}" Value ="Online">
<Setter Property="Foreground" Value="Black" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.Resources>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}" >
<Setter Property="ContextMenu" Value="{StaticResource ItemContextMenu}" />
</Style>
</ListView.ItemContainerStyle>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock FontWeight="Bold" FontSize="14" Foreground="Black" HorizontalAlignment="Center" Text="{Binding Name}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</Grid>
[what it should look like][1]
![1]: https://i.stack.imgur.com/yu0Rx.png
{
InitializeComponent();
List<User> items = new List<User>();
items.Add(new User() { Icon = ".\images\\white.png", UserName = "Tim Black", MessageColor ="Black", Status = Status.Online });
items.Add(new User() { Icon = ".\white.png", UserName = "Colin Black", MessageColor = "Black", Status = Status.Online });
items.Add(new User() { Icon = ".\images\\white.png", UserName = "Fred in Red", MessageColor = "Red" , Status = Status.Offline });
lvUsers.ItemsSource = items;
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(lvUsers.ItemsSource);
PropertyGroupDescription groupDescription = new PropertyGroupDescription("Status");
view.GroupDescriptions.Add(groupDescription);
}
private void MenuItemDelete_Click(object sender, RoutedEventArgs e)
{
Console.WriteLine("context Pressed as source" + e.Source.ToString());
}
}
public enum Status { Online, Offline };
public class User
{
public string Icon { get; set; }
public string UserName { get; set; }
public string MessageColor { get; set; }
public Status Status { get; set; }
}
I got this working.
Add a new class to your project StatusToForeGroundConverter.
public class StatusToForeGroundConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if(value is Status status)
{
return status == Status.Online ? "black" : "red";
}
//fallback
return "black";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Add the converter to your XAML:
<Window.Resources>
<local:StatusToForeGroundConverter x:Key="StatusConverter"/>
</Window.Resources>
Then just bind to it like:
<TextBlock Margin="10,0,0,0" Text="{Binding UserName}" Foreground="{Binding Status,Converter={StaticResource StatusConverter}}"/>
Full xaml:
<Grid>
<ListView x:Name="lvUsers" ItemsSource="{Binding items}">
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Icon}"/>
<TextBlock Margin="10,0,0,0" Text="{Binding UserName}" Foreground="{Binding Status,Converter={StaticResource StatusConverter}}"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
<ListView.Resources>
<ContextMenu x:Key="ItemContextMenu">
<MenuItem Header="More Info" Click="MenuItemDelete_Click" Command="{Binding Path=DataContext.MoreInfo, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}" Background="WhiteSmoke" />
</ContextMenu>
</ListView.Resources>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}" >
<Setter Property="Foreground" Value="{Binding MessageColor}" />
</Style>
</ListView.ItemContainerStyle>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock FontWeight="Bold" FontSize="14" Foreground="Black" HorizontalAlignment="Center" Text="{Binding Name}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</Grid>
Related
Non MVVM.
I got this ObservableCollection machines which is made of Machine-type objects:
[Serializable]
[NotifyPropertyChanged]
public abstract class Machine
{
public MachineNames MachineType { get; set; }
public int MachineVersion { get; set; }
public string LatestEditorName { get; set; }
public DateTime LatestSaveTime { get; set; }
public ObservableCollection<Parameter> Parameters { get; set; }
public string Notes { get; set; }
public abstract double CalculateThroughPut();
public Machine()
{
string[] nameParts = this.GetType().Name.Split('_');
Enum.TryParse(nameParts[0], out MachineNames currentMachineType);
MachineType = currentMachineType;
MachineVersion = int.Parse(nameParts[1]);
Parameters = new ObservableCollection<Parameter>();
ICollectionView icv = CollectionViewSource.GetDefaultView(Parameters);
icv.GroupDescriptions.Add(new PropertyGroupDescription("Group"));
LatestEditorName = Environment.UserName;
LatestSaveTime = DateTime.Now;
}
public double getValue(string parameterName)
{
Parameter currentParameter = Parameters.Where(x => x.Name == parameterName).First();
return currentParameter.Value * currentParameter.MetricConversionFactor;
}
And here's the declaration of it:
public partial class MainWindow : MetroWindow
{
IEnumerable<string> namesOfExistingMachines { get; set; }
public ObservableCollection<Machine> machines { get; set; }
and later on:
private void InitializeData()
{
machines = new ObservableCollection<Machine>();
this.DataContext = machines;
tcMainTabControl.ItemsSource = machines;
Notice please the [NotifyPropertyChanged] tag which is part of PostSharp, and simply makes all the properties of Machine changenotifiable for binding. in addition it makes all the properties of the properties changenotifiable.
Here's the initial window part of the XAML:
<Grid>
<Controls:MetroAnimatedTabControl Name="tcMainTabControl">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="ToolTipService.ShowDuration" Value="100000"/>
<Setter Property="ToolTipService.InitialShowDelay" Value="0"/>
<Setter Property="Header" Value="{Binding Converter={StaticResource tabHeaderConverter}}"/>
<Setter Property="ToolTip">
<Setter.Value>
<StackPanel Orientation="Vertical">
<TextBlock HorizontalAlignment="Center" Margin="0,10" FontSize="40" FontWeight="Bold" Text="{Binding Path=MachineType}"/>
<Image HorizontalAlignment="Center" Source="{Binding Path=MachineType, Converter={StaticResource imageUriConverter}}"/>
<StackPanel HorizontalAlignment="Center" Margin="0,10" Orientation="Horizontal">
<TextBlock FontSize="20" Text="Throughput model version "/>
<TextBlock FontSize="20" Text="{Binding Path=MachineVersion}"/>
</StackPanel>
<StackPanel HorizontalAlignment="Center" Margin="0,10" Orientation="Horizontal">
<TextBlock FontSize="20" Text="created by "/>
<TextBlock FontSize="20" Text="{Binding Path=LatestEditorName}"/>
<TextBlock FontSize="20" Text=" on "/>
<TextBlock FontSize="20" Text="{Binding Path=LatestSaveTime, StringFormat=dd/MM/yyyy}"/>
</StackPanel>
<TextBlock Margin="0,10" FontSize="20" Text="{Binding Path=Notes}"/>
</StackPanel>
</Setter.Value>
</Setter>
</Style>
</TabControl.ItemContainerStyle>
<Controls:MetroAnimatedTabControl.ContentTemplate>
<DataTemplate>
<DockPanel LastChildFill="True" Margin="10,0">
<StackPanel Orientation="Vertical">
<Border BorderBrush="{DynamicResource AccentColorBrush}" BorderThickness="1,1,1,1" CornerRadius="8,8,8,8" Margin="0,20" HorizontalAlignment="Center">
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock FontSize="20" Text="Throughput: "/>
<TextBlock FontSize="20" Text="{Binding Converter={StaticResource throughputCalculationConverter}, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock FontSize="20" Text=" panel sides per hour"/>
</StackPanel>
</Border>
<ListView DockPanel.Dock="Left" ScrollViewer.VerticalScrollBarVisibility="Auto" ItemsSource="{Binding Path=Parameters, UpdateSourceTrigger=PropertyChanged}">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Margin" Value="0,0,0,5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True" BorderBrush="Black" BorderThickness="0,0,0,1">
<Expander.Header>
<TextBlock FontSize="20" FontWeight="Bold">
<Run>Discipline: </Run>
<TextBlock Text="{Binding Path=Name, Converter={StaticResource titleCaseConverter}}"/>
</TextBlock>
</Expander.Header>
<Expander.Content>
<Border Margin="2" CornerRadius="3">
<ItemsPresenter />
</Border>
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock FontSize="20" Text="{Binding Name}" Margin="0,0,10,0" VerticalAlignment="Center"/>
<TextBox FontSize="20" BorderBrush="Black" BorderThickness="0,0,0,1" Background="Transparent" Controls:TextBoxHelper.Watermark="Enter value" Text="{Binding Value, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Margin="0,0,10,0" VerticalAlignment="Center" HorizontalContentAlignment="Center"/>
<TextBlock FontSize="20" Text="{Binding Unit}" VerticalAlignment="Center"/>
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="ToolTip" Value="{Binding Path=Notes}"/>
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
</StackPanel>
Everything is OK in the binding department. What I'd like is to invoke the CollectionChanged event (or something like that) for machines each time a property inside one of its Machine members changes property internally. In other words: if I change, for example, a Parameter inside Parameters inside one of the Machines of machine, I'd like it to update the calculation on
<TextBlock FontSize="20" Text="{Binding Converter={StaticResource throughputCalculationConverter}, UpdateSourceTrigger=PropertyChanged}"/>
Thanks!
To propagate the PropertyChanged notifications from the items inside the collection, you need the collection class that subscribes to the change notifications of its items. The standard ObservableCollection<T> class doesn't do that. You can extend the ObservableCollection<T> as shown below. You can also find more similar examples on SO (e.g. ObservableCollection that also monitors changes on the elements in collection).
[NotifyPropertyChanged]
public class ObservableCollectionEx<T> : ObservableCollection<T>
{
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Remove)
{
foreach (T item in e.OldItems)
{
((INotifyPropertyChanged) item).PropertyChanged -= OnItemPropertyChanged;
}
}
else if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (T item in e.NewItems)
{
((INotifyPropertyChanged) item).PropertyChanged += OnItemPropertyChanged;
}
}
base.OnCollectionChanged(e);
}
protected void OnPropertyChanged(string propertyName)
{
base.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
NotifyPropertyChangedServices.SignalPropertyChanged(this, "Item[]");
NotifyCollectionChangedEventArgs collectionChangedEventArgs = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
base.OnCollectionChanged(collectionChangedEventArgs);
}
}
When you use this custom collection class, the collection will raise an event when a property of an item changes. Now you can also tell PostSharp to propagate this notification as a change of the collection property itself using [AggregateAllChanges] attribute applied to the collection property (e.g. Parameters, machines).
[AggregateAllChanges]
public ObservableCollectionEx<Parameter> Parameters { get; set; }
[AggregateAllChanges]
public ObservableCollectionEx<Machine> machines { get; set; }
I like to Binding the datagrid parent from Behavior (pnlDgSubFooter) placed in DataGrid/GroupStyle/ContainerStyle/Style[GroupItem]/Setter[template]/ControlTemplate/Expander/Header/DockPanel/StackPanel
But the Binding can't leave of DockPanel.
in ElementName, there is only "btnExpandAll" and himself.
In Behavior, the LeDataGrid property return alway null (except if i bind on . or btnExpandAll)
<DataGrid Grid.Row="0" x:Name="dgMain" AutoGenerateColumns="False" SelectionUnit="FullRow" LoadingRow="dgMain_LoadingRow" MouseDown="dgMain_MouseDown" Sorting="dgMain_Sorting"
CanUserReorderColumns="False" CanUserResizeColumns="True" CanUserResizeRows="False" CanUserSortColumns="True" CanUserAddRows="False"
Style="{StaticResource dg}" RowStyle="{StaticResource dgRow}" CellStyle="{StaticResource dgCell}" ColumnHeaderStyle="{StaticResource dgColHeader}" RowHeaderStyle="{StaticResource dgRowHeader}"
ItemsSource="{Binding NotifyOnSourceUpdated=True, Source={StaticResource cvsElmts}}" HorizontalAlignment="Left" >
<!--DataGrid.DataContext><Binding Source="{StaticResource tblUsers}"/></DataGrid.DataContext-->
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<mvvm:EventToCommand Command="{Binding SendCommand, Mode=OneWay}" CommandParameter="{Binding SelectedItem, ElementName=dgMain}" PassEventArgsToCommand="False"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGrid.GroupStyle>
<GroupStyle>
<!-- Style for groups under the top level. -->
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Margin" Value="0,0,0,5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="{Binding DataContext.ExpandedAll, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" BorderThickness="1,1,1,5">
<Expander.Style>
<Style TargetType="{x:Type Expander}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsBottomLevel}" Value="True">
<Setter Property="Margin" Value="8,0,0,0" />
</DataTrigger>
</Style.Triggers>
</Style>
</Expander.Style>
<Expander.Header>
<DockPanel>
<Button Name="btnExpandAll" Command="{Binding DataContext.ExpandedAllCommand, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" Click="btnExpandAll_Click" ToolTip="{x:Static resx:resMain.lblExpandAll}" BorderThickness="0">
<TextBlock Grid.Column="0" Text="" FontFamily="{StaticResource FntSymbol}" Foreground="{StaticResource scbBlack}" FontSize="12" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Button>
<TextBlock FontWeight="Bold" Text="" Margin="5,0,0,0"><Run Text="{Binding Path=Name, Mode=OneWay}" /><Run Text=" ("/><Run Text="{Binding Path=ItemCount, Mode=OneWay}" /><Run Text=" éléments)."/></TextBlock>
<StackPanel Name="pnlDgSubFooter" HorizontalAlignment="Left" Orientation="Horizontal" >
<i:Interaction.Behaviors>
<Classes:BehaviorWithCommand LeDataGrid="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}" LeGroupe="{Binding .}" />
</i:Interaction.Behaviors>
</StackPanel>
</DockPanel>
</Expander.Header>
<Expander.Content>
<Border BorderThickness="1" BorderBrush="{StaticResource scbGrey3}">
<ItemsPresenter />
</Border>
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
....
The Behavior:
public class BehaviorWithCommand : Behavior<FrameworkElement> { // StackPanel
public static readonly DependencyProperty LeDataGridProperty = DependencyProperty.Register(nameof(LeDataGrid), typeof(object), typeof(BehaviorWithCommand), new PropertyMetadata(null));
public static readonly DependencyProperty LeGroupeProperty = DependencyProperty.Register(nameof(LeGroupe), typeof(object), typeof(BehaviorWithCommand), new PropertyMetadata(null));
public object LeDataGrid {
get { return (object)GetValue(LeDataGridProperty); }
set { SetValue(LeDataGridProperty, value); }
}
public object LeGroupe {
get { return (object)GetValue(LeGroupeProperty); }
set { SetValue(LeGroupeProperty, value); }
}
protected override void OnAttached() {
base.OnAttached();
((Panel)AssociatedObject).Children.Clear();
var dg = new DataGrid();
dg.Columns.Add(new DataGridTextColumn());
dg.Columns.Add(new DataGridTextColumn());
dg.Columns.Add(new DataGridTextColumn());
//var dg = (DataGrid)LeDataGrid;
foreach (var item in dg.Columns) {
var g = new Grid() { MinWidth = 10 };
g.SetBinding(Grid.WidthProperty, new System.Windows.Data.Binding("ActualWidth") { Source = item }); // dhHeadName DataGridColumn
var t = new TextBox() { Margin = new Thickness(1, 0, 1, 0), Background = new SolidColorBrush(Color.FromRgb(200, 200, 200)), FontWeight = FontWeights.Bold, FontSize = 9, IsReadOnly = true };
t.Text = "Coucou";
g.Children.Add(t);
((Panel)AssociatedObject).Children.Add(g);
}
}
protected override void OnDetaching() {
base.OnDetaching();
}
}
This is working:
<StackPanel Grid.ColumnSpan="2" Grid.Row="1" Name="pnlDgSubFooter" HorizontalAlignment="Left" Orientation="Horizontal" Margin="-20, 0, 0, 0">
<i:Interaction.Behaviors>
<Classes:BehaviorWithCommand LeDataGrid="{Binding ., ElementName=dgMain}" LeGroupe="{Binding .}" />
</i:Interaction.Behaviors>
</StackPanel>
It's just that designer don't resolve it.
Regards.
I am currently working on a WPF application which contains a datagrid with 3 columns containing ToggleButtons whose code is as follows
<DataGridTemplateColumn Header="Closed" Width="60">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ToggleButton>
<ToggleButton.Content>
...
</ToggleButton.Content>
</ToggleButton>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Checked" Width="60">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ToggleButton Visibility="{Binding Path=IsClosedProperty, Converter={StaticResource toggleButtonVisibilityConverter}}">
<ToggleButton.Content>
...
</ToggleButton.Content>
</ToggleButton>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Active" Width="60">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ToggleButton Visibility="{Binding Path=IsCheckedProperty, Converter={StaticResource toggleButtonVisibilityConverter}}">
<ToggleButton.Content>
...
</ToggleButton.Content>
</ToggleButton>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
The code of the toggleButtonVisibilityConverter is as follows
public class ToggleButtonVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value is bool && ((bool)value) ? Visibility.Visible : Visibility.Hidden;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
The application should work such that when ToggleButton of a row in the Closed column is clicked, the ToggleButton in the Checked column of that row becomes visible(i.e, toggleButtonVisibilityConverter is called). Also when ToggleButton of a row in the Checked column is clicked, the ToggleButton in the Active column of that row becomes visible. And when the respective ToggleButtons are unclicked they should become hidden in the same hierarchy.
The IsClosedProperty and the IsCheckedProperty are boolean properties of ViewModel which are set true when Closed and Checked toggleButtons are clicked respectively.
Class to hold data
public class GridToggleButtonItem
{
private bool _isClosedProperty;
public bool IsClosedProperty
{
get { return _isClosedProperty; }
set { _isClosedProperty = value; }
}
private bool _isCheckedProperty;
public bool IsCheckedProperty
{
get { return _isCheckedProperty; }
set { _isCheckedProperty = value; }
}
}
ViewModel that has items collection to be bound to datagrid
public class GridToggleButtonViewModel
{
public List<GridToggleButtonItem> Items { get; set; }
public GridToggleButtonViewModel()
{
Items = new List<GridToggleButtonItem>()
{
new GridToggleButtonItem() { IsCheckedProperty = false, IsClosedProperty = false},
new GridToggleButtonItem() { IsCheckedProperty = false, IsClosedProperty = false},
new GridToggleButtonItem() { IsCheckedProperty = false, IsClosedProperty = false},
new GridToggleButtonItem() { IsCheckedProperty = false, IsClosedProperty = false},
new GridToggleButtonItem() { IsCheckedProperty = false, IsClosedProperty = false}
};
}
}
XAML code that binds to viewmodel
<Window x:Class="StackOverFlowQ.GridToggleButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:StackOverFlowQ"
Title="GridToggleButton" Height="500" Width="500">
<Window.DataContext>
<local:GridToggleButtonViewModel></local:GridToggleButtonViewModel>
</Window.DataContext>
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"></BooleanToVisibilityConverter>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Margin" Value="3"></Setter>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="FontWeight" Value="Bold"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel>
<DataGrid ItemsSource="{Binding Path=Items}" AutoGenerateColumns="False" SelectionMode="Single" SelectionUnit="Cell" CanUserAddRows="False" >
<DataGrid.Columns>
<DataGridTemplateColumn Header="Closed" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ToggleButton Content="Closed" IsChecked="{Binding Path=IsClosedProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></ToggleButton>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Checked" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ToggleButton Content="Checked" Visibility="{Binding Path=IsClosedProperty, Converter={StaticResource ResourceKey=BooleanToVisibilityConverter}}" IsChecked="{Binding Path=IsCheckedProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></ToggleButton>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Active" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ToggleButton Content="Active" Visibility="{Binding Path=IsCheckedProperty, Converter={StaticResource ResourceKey=BooleanToVisibilityConverter}}"></ToggleButton>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
<Grid>
<Grid.Resources>
<FrameworkElement x:Key="ProxyElement" DataContext="{Binding DataContext, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListBoxItem}}" />
</Grid.Resources>
<ContentControl Content="{StaticResource ProxyElement}" Visibility="Collapsed" />
<DataGrid
ItemsSource="{Binding SomeData}">
<DataGrid.Columns>
<DataGridTemplateColumn IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image MaxWidth="16"
MaxHeight="16"
Source="/abc.png" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn MinWidth="80" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Foreground="Black" Text="{Binding Name}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn MinWidth="64"
Binding="{Binding Category}"
Foreground="Black"
IsReadOnly="True"
Visibility="{Binding Path=DataContext.IsCategoryColumnVisible,
Source={StaticResource ProxyElement},
Converter={StaticResource BooleanVisibilityConverter}}" />
<DataGridTextColumn MinWidth="64"
Binding="{Binding Size}"
Foreground="Black"
IsReadOnly="True"
Visibility="{Binding Path=DataContext.IsSizeColumnVisible,
Source={StaticResource ProxyElement},
Converter={StaticResource BooleanVisibilityConverter}}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
DataGridColumns are not part of Visual Tree, so your binding will not work. Here is my solution.
Create a proxy object and bind its DataContext to correct object which should be in DataContext(your ViewModel).
Put it in a ContentControl and make it hidden.
Use your proxy object as Source of your binding.
I have a listview:
<ListView Name="SelectedFeeds">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}"
BasedOn="{StaticResource {x:Type ListViewItem}}">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" Name="panel">
<TextBlock x:Name="Title" FontSize="24"
Text="{Binding Title, IsAsync=True}" TextWrapping="Wrap" />
<Label x:Name="PubDate" FontSize="10"
Content="{Binding Path=PubDate, IsAsync=True}" />
<TextBlock my:HtmlParser.HTMLText=
"{Binding Path=Description, IsAsync=True}"
TextWrapping="Wrap"
Width="{Binding ElementName=panel,
Path=ActualWidth}" Height="0" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
There is a TextBlock in DataTemplate with large height.
At start I'm setting height of this textblock to "0" and when ListViewItem is selected, I need to set that height to "Auto".
This is probably can be done with triggers, but I can't figure it out.
For a trigger approach, use it in the DataTemplate.
<ListView Name="SelectedFeeds">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" Name="panel">
<TextBlock x:Name="Title" FontSize="24"
Text="{Binding Title, IsAsync=True}" TextWrapping="Wrap" />
<Label x:Name="PubDate" FontSize="10"
Content="{Binding Path=PubDate, IsAsync=True}" />
<TextBlock x:Name="TextHolder"
my:HtmlParser.HTMLText=
"{Binding Path=Description, IsAsync=True}"
TextWrapping="Wrap"
Width="{Binding ElementName=panel,
Path=ActualWidth}" Height="0" />
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListViewItem}}}" Value="True">
<Setter TargetName="TextHolder" Property="Height" Value="123"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Note the x:Name property on the TextBlock.
You may be able to do this with a trigger (although I don't know how at the moment), but you can do this with a converter instead.
public class BoolToLengthConverter : IValueConverter
{
public BoolToLengthConverter()
{
TrueValue = double.NaN;
FalseValue = 0;
}
[TypeConverter(typeof(LengthConverter))]
public double TrueValue { get; set; }
[TypeConverter(typeof(LengthConverter))]
public double FalseValue { get; set; }
#region Implementation of IValueConverter
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
return System.Convert.ToBoolean(value) ? TrueValue : FalseValue;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return TrueValue.Equals(value);
}
#endregion
}
Xaml
<ListView.Resources>
<Converters:BoolToLengthConverter
x:Key="BoolToHeightConverter" TrueValue="Auto" FalseValue="0" />
</ListView.Resources>
<TextBlock ... Height="{Binding Path=IsSelected,
Converter={StaticResource BoolToHeightConverter},
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type ListViewItem}}}"/>
You could of course achieve the same result by using a BoolToVisibilityConverter and binding to the TextBlock's Visibility property.
I am new to WPF and entity framework. I have ran into the the following problem while working on an application. In my application I am binding my data using collection view source from entity framework. one of my database table has a column named isNumeric datatype Boolean. if its is true then my grid view in WPF window should show the text "Numeric" and "String" if false. For this requirement I cant directly bind the my linq query result with the grid view or any control in the UI. Any thoughts on how could I solve this.
Here some of my codes
MainWindow.XAML.cs code
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private PartNumbersEntities partNumberContext = new PartNumbersEntities();
private PartNumbersCollection partNumberData;
//private PartClassesCollection partClassData;
private CollectionViewSource MasterViewSource;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
//string classFilter = classNameTextBox.Text;
//if (classFilter.Length == 0)
classFilter = "Dis";
//MessageBox.Show(classFilter);
var result = partNumberContext.PartNumbers.Where(p => p.PartClass.chrPCName.Contains(classFilter)).Select(p => p);
this.partNumberData = new PartNumbersCollection(result, partNumberContext);
this.MasterViewSource = (CollectionViewSource)this.FindResource("MasterView");
this.MasterViewSource.Source = this.partNumberData;
}
}
My partNumber Collection
class PartNumbersCollection : ObservableCollection<PartNumber>
{
private PartNumbersEntities _context;
public PartNumbersEntities Context
{
get { return _context; }
}
public PartNumbersCollection(IEnumerable<PartNumber> partNumbers, PartNumbersEntities context)
: base(partNumbers)
{
_context = context;
}
}
Xaml Code
<Window x:Class="Engenious.PartNumbersUI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="454" Width="1033" Loaded="Window_Loaded">
<Window.Resources>
<CollectionViewSource x:Key="MasterView" />
<CollectionViewSource x:Key="PartProperties"
Source="{Binding Source={StaticResource MasterView},
Path='PartProperties'}"/>
<!--<CollectionViewSource x:Key="PartNumberView"
Source="{Binding Source={StaticResource MasterView},
Path='PartNumbers'}"/>-->
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource MasterView}}">
<Grid.RowDefinitions>
<RowDefinition Height="42" />
<RowDefinition Height="310" />
<RowDefinition Height="42" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Name="Grid0">
<StackPanel Name="StackPanel1" Orientation="Horizontal">
<Label Content="Class Filter" Height="28" Name="label1" Margin="3" />
<TextBox Height="28" Name="classNameTextBox" Width="120" Margin="3"/>
<Button Content="Apply" Height="28" Name="applyButton" Width="80" Margin="3"/>
</StackPanel>
</Grid>
<ListView Grid.Row="1" Name="ListView1" VerticalContentAlignment="Top"
VerticalAlignment="Top" HorizontalAlignment="Left" Height="310"
Width="320"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding }">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Header="Part Class Name" Width="150">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding Path=PartClass.chrPCName}" Margin="-6,0,-6,0"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Part Number" Width="130">
<GridViewColumn.CellTemplate>
<DataTemplate>
<!--<Label Content="{Binding Source={StaticResource PartNumberView},Path=chrPNPartNumber}" Margin="-6,0,-6,0"/>-->
<Label Content="{Binding Path=chrPNPartNumber}" Margin="-6,0,-6,0"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
<ListView Grid.Row="1" Height="310" HorizontalAlignment="Left" Margin="350,0,0,0" Name="listView2"
VerticalAlignment="Top" Width="375"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Source={StaticResource PartProperties}}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="150">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding Path=ConfigurationProperty.chrCPProperty}" Margin="-6,0,-6,0"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Datatype" Width="130">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding Path=ConfigurationProperty.bitCPIsNumeric}" Margin="-6,0,-6,0"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
<StackPanel Name="StackPanel4" Orientation="Horizontal" Grid.Row="2">
<Button Height="25" Name="btnAddDetail" Width="82" Margin="3">Save</Button>
<Button Height="26" Name="btnDeleteDetail" Width="83" Margin="3">Delete</Button>
</StackPanel>
</Grid>
Here is a Screen shot
I have used data Trigger to solve this problem..
Here is my XAML for it.
<GridViewColumn Header="Datatype" Width="130">
<GridViewColumn.CellTemplate>
<DataTemplate >
<Label>
<Label.Resources>
<Style TargetType="{x:Type Label}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path = 'ConfigurationProperty.bitCPIsNumeric'}" Value="True">
<Setter Property="Content" Value="Numeric" />
</DataTrigger>
<DataTrigger Binding="{Binding Path = 'ConfigurationProperty.bitCPIsNumeric'}" Value="False">
<Setter Property="Content" Value="String" />
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Resources>
</Label>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>