I am working with a Wpf/XAML based application which has DataGrid with one of the DataGridColumn containing the TreeView control to let user select the item required.
<DataGrid.Columns>
<DataGridTextColumn Header="SerialNumber" Width="*" Binding="{Binding SerialNumber}" />
<DataGridTemplateColumn Header="Field" Width="*">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<uControls:FieldTreeViewControl />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding CurrentField.FieldName,Mode=TwoWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Field column in above code is the one which displays treeviewcontrol in the DataGrid cell by referencing the FieldTreeViewControl and it works perfectly. The xaml code of FieldTreeViewControl is :
<UserControl>
.......
<Grid>
<TreeView x:Name="MyUITreeView" ItemsSource="{Binding Fields}">
<TreeView.Resources>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsNodeExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsNodeSelected, Mode=TwoWay}" />
</Style>
<DataTemplate x:Key="NormalTemplate">
<StackPanel Orientation="Horizontal">
<!--<TextBlock Text="-" Margin="3"/>-->
<TextBlock Text="{Binding FieldName}" Margin="3"/>
<StackPanel.ContextMenu>
<ContextMenu Name="myChildMenu" DataContext="{Binding PlacementTarget,RelativeSource={RelativeSource Self}}">
<MenuItem Header="Add Field" Command="{Binding DataContext.AddFieldCommand}" CommandParameter="{Binding}">
</MenuItem>
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>
</DataTemplate>
<!--<DataTemplate x:Key="EditTemplate">
<TextBox Text="{Binding FieldName}"/>
</DataTemplate>-->
</TreeView.Resources>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<ContentPresenter Content="{Binding}">
<ContentPresenter.Style>
<Style TargetType="{x:Type ContentPresenter}">
<Setter Property="ContentTemplate" Value="{StaticResource NormalTemplate}"/>
<Style.Triggers>
<DataTrigger
Binding="{Binding IsNodeSelected,
RelativeSource={RelativeSource
FindAncestor,
AncestorType={x:Type TreeViewItem}}}"
Value="True">
</DataTrigger>
</Style.Triggers>
</Style>
</ContentPresenter.Style>
</ContentPresenter>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
Now when user completes selecting an item from treeview I want to collapse treeview to show only that selected item. Later when user wants to change his selection , then the treeview should be available again by clicking on that column/cell. Is that possible to do this in wpf/XAML ? I tried to follow the link but not able to implement it for my scenario.
Related
I am creating a list of CheckBoxs and a ComboBox that contains the list of checked checkboxes in WPF MVVM application. I don't know how to bind in text of combobox checked values from checkboxes.
Here is what I have tried:
<ComboBox ItemsSource="{Binding Systems}" Grid.Row="4" Grid.Column="1"
IsEditable="True" IsReadOnly="True" Text="{}">
<ComboBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding TemplateName}" IsChecked="{Binding
IsSystemChecked, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
This should do the trick:
<StackPanel>
<ListView ItemsSource="{Binding Systems}" >
<ListView.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding TemplateName}" IsChecked="{Binding
IsSystemChecked, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ComboBox ItemsSource="{Binding Systems}" DisplayMemberPath="TemplateName" >
<ComboBox.Style>
<Style TargetType="ComboBox">
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="ComboBoxItem" BasedOn="{StaticResource {x:Type ComboBoxItem}}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSystemChecked}" Value="False">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</Setter.Value>
</Setter>
</Style>
</ComboBox.Style>
</ComboBox>
</StackPanel>
Here there is:
ListView containing a checkbox for each item in Systems which is bound to to the IsSystemChecked property
ComboBox containing all of the items in Systems however if the IsSystemChecked property is false the Visibility is set to Collapsed so it is not displayed
Let me know if you have any issues! Hope this helps.
In my WPF MVVM project I use INotifyDataErrorinfo to handle validation within a DataGrid. I can successfully style error cells in my "Operator" column thus:
<DataGridTemplateColumn Header="Operator" Width="140">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Operator}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding OperatorId, Converter={StaticResource IsOperatorIdNullConverter}}" Value="False">
<Setter Property="FontWeight" Value="Bold"/>
</DataTrigger>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip DataContext="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget}">
<ItemsControl ItemsSource="{Binding Path=(Validation.Errors)}" DisplayMemberPath="ErrorContent"/>
</ToolTip>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="Salmon"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<Grid>
<controls:AutoCompleteBox Text="{Binding Operator, UpdateSourceTrigger=LostFocus, Mode=TwoWay}"
ItemsSource="{Binding Path=Data.OperatorNames, Source={StaticResource proxy}}"
IsTextCompletionEnabled="True"
FilterMode="Contains"
MinimumPrefixLength="3"/>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
In my "OperatorType" column however this same technique doesn't work. Errors are being detected and the system default error styling is shown but my custom styling isn't. The code is:
<DataGridTemplateColumn Header="Operator type" Width="140">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding OperatorType.OperatorTypeName}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip DataContext="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget}">
<ItemsControl ItemsSource="{Binding Path=(Validation.Errors)}" DisplayMemberPath="ErrorContent"/>
</ToolTip>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="Salmon"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<Grid>
<controls:AutoCompleteBox ItemsSource="{Binding Path=Data.OperatorTypeNames, Source={StaticResource proxy}}"
ItemTemplate="{StaticResource AutoCompleteBoxItemOperatorTypeTemplate}"
SelectedItem="{Binding OperatorType, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
ValueMemberPath="OperatorTypeName"
IsTextCompletionEnabled="True"
FilterMode="Contains"
MinimumPrefixLength="3"/>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
The only differences as far as I can see are that:
The Text in "Operator" is using bound to a POCO (Operator) while for "OperatorType" it is bound to a POCO property (OperatorType.OperatorTypeName)
The AutoCompleteBox declarations are slightly different
I've tried numerous settings for the ToolTip DataContext but nothing seems to work.
Question
What do I need to change to get the "OperatorType" customised error styling to work?
Well, it was a bit of a journey, but the solution was to set a DataContext on the TextBlock:
<TextBlock DataContext="{Binding OperatorType}" Text="{Binding OperatorTypeName}">
This causes the Trigger to be pointed at the OperatorType POCO whereas the Text in the box is taken from OperatorType.OperatorTypeName.
I have a C# WPF-Application. In the XAML i have a Datagrid, to which I have added a ContextMenu, that looks like this:
<DataGrid.ContextMenu>
<ContextMenu ItemsSource="{Binding Categories}">
<ContextMenu.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding Name}" Background="{Binding Brush}" Click="MenuItem_Click" Tag="{Binding Id}" />
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</DataGrid.ContextMenu>
When using the application it looks like this: http://imgur.com/3UTj1Xd
The problem is, that when clicking the color part of the box (which I'm guessing is part of some internal grid) the MenuItem_Click event is fired. However, when I'm clicking on the grey part of the MenuItem the click event is not fired. Does anyone know why it behaves this way? And is there a way to fix this?
Additionally, it would be great to be able to color the whole menu item, not just the small box inside. Is there a way to do this?
You could try to decrease the Padding of the ContextMenu and the BorderThickess of the MenuItems:
<DataGrid.ContextMenu>
<ContextMenu ItemsSource="{Binding Categories}" Padding="0">
<ContextMenu.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding Name}" Background="{Binding Brush}" Click="MenuItem_Click" Tag="{Binding Id}"
BorderThickness="0"/>
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</DataGrid.ContextMenu>
Or
<DataGrid.ContextMenu>
<ContextMenu ItemsSource="{Binding Categories}" Padding="0">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Name}" />
<Setter Property="Tag" Value="{Binding Id}" />
<Setter Property="Background" Value="{Binding Brush}" />
<EventSetter Event="Click" Handler="MenuItem_Click" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</DataGrid.ContextMenu>
I'm trying to compose a data grid (in an MVVM 4.5 project) that will show my data grouped by a specific property (by use of expanders). This all works but I'd like to add a context menu with the options "Expand All" and "Collapse All" which will collapse/expand all the groups. The event handlers for this menu item click events are being handled in the window's code-behind.
The problem is my context menu is applied to the expander and thus inherited by all its children which includes the <ItemsPresenter/> and this all rows/cells.
I want to only apply the context menu to the grouped header itself. This is achievable if it's applied to the innards (like the StackPanel in the example below) but in that case, the context menu isn't accessible for the entire Header line, only on the StackPanel contents/text.
I'm planning on using a different context menu for the items themselves (add/edit etc) and have a Collapse/Expand context menu only apply to the group header. Is this achievable?
<DataGrid Name="dgData" ItemsSource="{Binding MyItems}" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False">
<DataGrid.Resources>
<!--GroupHeader Text-->
<Style x:Key="gridGroupTextStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="12"/>
<Setter Property="FontWeight" Value="Bold"/>
</Style>
<!--GroupHeader ContextMenu-->
<ContextMenu x:Key="cm_columnHeaderMenu">
<MenuItem Name="mi_ExpandAll" Header="Expand groups"/>
<MenuItem Name="mi_CollapseAll" Header="Collapse groups"/>
</ContextMenu>
<!--Grouping style-->
<Style x:Key="filesGroupHeaderStyle" TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander x:Name="exp" IsExpanded="true">
<Expander.Style>
<Style TargetType="{x:Type Expander}">
<Setter Property="ContextMenu" Value="{StaticResource cm_columnHeaderMenu}"/>
</Style>
</Expander.Style>
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" Style="{StaticResource gridGroupTextStyle}"/>
<TextBlock Text=" (" Style="{StaticResource gridGroupTextStyle}"/>
<TextBlock Text="{Binding ItemCount}" Style="{StaticResource gridGroupTextStyle}"/>
<TextBlock Text=")" Style="{StaticResource gridGroupTextStyle}"/>
</StackPanel>
</Expander.Header>
<ItemsPresenter/>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.Resources>
<DataGrid.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource filesGroupHeaderStyle}">
<GroupStyle.Panel>
<ItemsPanelTemplate>
<DataGridRowsPresenter/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</DataGrid.GroupStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="Column 1" Binding="{Binding Prop1}" IsReadOnly="True"/>
<DataGridTextColumn Header="Column 2" Binding="{Binding Prop2}" IsReadOnly="True"/>
<DataGridTextColumn Header="Column 3" Binding="{Binding Prop3}" IsReadOnly="True"/>
</DataGrid.Columns>
</DataGrid>
If i understand you right, you want to click anywhere in your StackPanel and get the ContextMenu to work like this:
If yes, here we go in code:
<Expander Grid.Row="1">
<Expander.Style>
<Style TargetType="{x:Type Expander}">
<Setter Property="ContextMenu" Value="{x:Null}"/>
</Style>
</Expander.Style>
<Expander.Header>
<StackPanel Orientation="Horizontal" Width="{Binding Path=ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Expander}}}" Background="Pink" HorizontalAlignment="Stretch">
<StackPanel.Style>
<Style TargetType="StackPanel" >
<Setter Property="ContextMenu" Value="{StaticResource cm_columnHeaderMenu}"></Setter>
</Style>
</StackPanel.Style>
<TextBlock Text="Hello" />
<TextBlock Text=" (" />
<TextBlock Text="world" />
<TextBlock Text=")" />
<TextBlock Text="{Binding Path=ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Expander}}}" />
</StackPanel>
</Expander.Header>
<TextBlock Text="Dummy"></TextBlock>
</Expander>
What did i do?
First, bind your Stackpanel-Width to actual width of Expander
Second, make the Stackpanel stretch.
Hope this helps
In my application, I have a CheckBox in it, I would like when it's checked, show one DataTemplate and when it's unchecked, show another one.
Here is the snippet with the two templates
<DataGrid x:Name="dataGrid" LoadingRow="dataGrid_LoadingRow_1" ItemsSource="{Binding Item3}"
<DataGrid.RowHeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding StudentId}"/>
</DataTemplate>
<DataTemplate>
<TextBlock Text="{Binding FullName}"/>
</DataTemplate>
</DataGrid.RowHeaderTemplate>
</DataGrid>
I'm not sure how to implement it, but I supposed that I need the interface INotifyPropertyChanged in my User control to fire or just determine when has changed.
You can do it only via triggers. If the above thing is your requirement. You can do it simply via triggers. I tried and it worked for me.
<Window.Resources>
<ControlTemplate x:Key="MyRowHeaderTemplate">
<TextBlock x:Name="RowHeaderTxt"
Text="{Binding StudentId, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Path=IsChecked, ElementName=MyCheckBox}"
Value="True">
<Setter TargetName="RowHeaderTxt" Property="Text"
Value="{Binding FullName, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Window.Resources>
<StackPanel>
<CheckBox x:Name="MyCheckBox"/>
<DataGrid ItemsSource="{Binding Item3}" AutoGenerateColumns="True">
<DataGrid.RowHeaderTemplate>
<DataTemplate>
<ContentControl Template="{StaticResource MyRowHeaderTemplate}"/>
</DataTemplate>
</DataGrid.RowHeaderTemplate>
</DataGrid>
</StackPanel>
Try this.