Trouble styling data-bound WPF ComboBoxItem - c#

I have a ComboBox, bound to a DataTable. I'm trying to add an extra ComboBoxItem to the top of the list, where I can put a link to customize the list. Currently I am just inserting a dummy row to the top of my DataTable, and then using a DataTrigger on the ComboBox to make it appear correctly. However, I'm not quite getting the correct result.
I've tried two methods. In the first, my DataTrigger replaces the dummy item with a ControlTemplate, which contains a TextBlock.
<ComboBox IsEditable="True" Name="comboWell" ItemsSource="{Binding}" DisplayMemberPath="wellId">
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding wellId}" Value="(settings)">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<TextBlock Text="Customize..." />
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
The result looks right, but there is no mouseover highlight on that item. The rest of the list works fine, but that one item doesn't react at all when I mouse over it. I've tried adding extra triggers and styles to apply a mouseover effect, but I get no change.
The second method I tried was just to alter the appearance of the item rather than completely replace it with a ControlTemplate.
<ComboBox IsEditable="True" Name="comboWell" ItemsSource="{Binding}" DisplayMemberPath="wellId">
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding wellId}" Value="(settings)">
<Setter Property="Content" Value="Customize..." />
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
This one functions like a regular list item, mouseover works fine. However, the item is blank. Neither the original text, nor the text I try to set in the DataTrigger, are there. No errors, just an empty list item.
Is there a better way to accomplish this?

Remove the DisplayMemberPath and add the default Content to the Style
<ComboBox IsEditable="True" Name="comboWell" ItemsSource="{Binding }">
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="Content" Value="{Binding wellId}" />
<Style.Triggers>
<DataTrigger Binding="{Binding wellId}" Value="(settings)">
<Setter Property="Content" Value="Customize..." />
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
DisplayMemberPath is actually a shortcut way of saying the item template should just be a TextBlock with it's Text bound to the DisplayMemberPath item, and I am guessing it was overwritting whatever you had in the Style.

Related

CellStyle based on RowStyle in WPF

I have a WPF DataGrid represented in XAML. I'm using a RowStyle for my grid's TableView but also need to set some properties for specific cells. I need those cells to have the properties of the row style and apply the extra properties from the cell style on top of those.
What I would need is something like this, although this doesn't work as it's saying:
Target type 'CellContentPresenter' is not convertible to base type 'GridRowContent'
<Style x:Key="MyGridRowStyle"
BasedOn="{StaticResource {themes:GridRowThemeKey ResourceKey=RowStyle}}"
TargetType="{x:Type dxg:GridRowContent}">
<Setter Property="Height"
Value="25" />
<Style.Triggers>
...
</Style.Triggers>
</Style>
<Style x:Key="MyCellStyle"
BasedOn="{StaticResource MyGridRowStyle}"
TargetType="{x:Type dxg:CellContentPresenter}">
<Style.Triggers>
...
</Style.Triggers>
</Style>
I've also tried not specifying the BasedOn property for MyCellStyle but that doesn't work either.
I use the MyCellStyle like this:
<dxg:GridColumn Header="My Header"
FieldName="MyFieldName"
Width="100"
CellStyle="{StaticResource MyCellStyle}" />
and MyGridRowStyle like this on the TableView:
RowStyle="{StaticResource MyGridRowStyle}"
How can I make the cell style only change the properties specified in MyCellStyle and use the values specified in MyGridRowStyle for the other properties?
You can't base a CellContentPresenter style on a GridRowContent style. These are two completely different types and just because they may happen to have some properties that have the same name these are still completely different and independant properties with no relationship to each other.
The best thing you can do is to define the common values as separate resources and use these resources in both styles, e.g.:
<Window.Resources>
<s:Double x:Key="commonHeight">25</s:Double>
<SolidColorBrush x:Key="commonBg">Red</SolidColorBrush>
<Style x:Key="MyCellStyle" TargetType="{x:Type dxg:CellContentPresenter}">
<Setter Property="Height" Value="{StaticResource commonHeight}" />
<Setter Property="Background" Value="{StaticResource commonBg}" />
</Style>
<Style x:Key="MyGridRowStyle" BasedOn="{StaticResource {themes:GridRowThemeKey ResourceKey=RowStyle}}" TargetType="{x:Type dxg:GridRowContent}">
<Setter Property="Height" Value="{StaticResource commonHeight}" />
<Setter Property="Background" Value="{StaticResource commonBg}" />
</Style>
</Window.Resources>
But you still need to define all setters in both styles.
based on normal WPF DataGrid you could try this and expand it for dxg
the class DataGridCell is derived from ContentControl (that is a child from Content).
the class DataGridRow is derived from Control.
now you could try the following:
<Style x:Key="BaseStyle" TargetType="Control" >
<!-- Maybe add BaseStyle / theme here with BasedOn -->
<Setter Property="Height" Value="25" />
<!-- Row and Column defaults -->
</Style>
<Style x:Key="MyGridRowStyle" BasedOn="{StaticResource BaseStyle}"
TargetType="DataGridRow">
<!-- Row specific implementation -->
</Style>
<Style x:Key="MyCellStyle" BasedOn="{StaticResource BaseStyle}"
TargetType="DataGridCell">
<!-- Column specific implementation -->
</Style>
summary:
use base type of both Row and Column classes for your BaseStyle and use this one as BasedOn. For dxg you can extend it by your own...
My understanding of the question: values of the cell style should change based off of values in the style of the row it is in.
Trigger off of value
Here is a simple working example (test it by changing the DataGridRow Background to Red, you will notice that the cell's foreground changes to Blue):
<DataGrid>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="White"/>
</Style>
</DataGrid.RowStyle>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGridRow, Mode=FindAncestor}, Path=Background}" Value="Red">
<Setter Property="Foreground" Value="Blue"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
</DataGrid>
You can do a similar binding to directly set properties of the CellStyle to the value of properties of the row it is in.
Relative bind directly to a property
<DataGrid x:Name="dataGrid1" ItemsSource="{Binding Collection}">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Height" Value="20"/>
</Style>
</DataGrid.RowStyle>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="FontSize" Value="{Binding RelativeSource={RelativeSource AncestorType=DataGridRow, Mode=FindAncestor}, Path=Height}"/>
</Style>
</DataGrid.CellStyle>
</DataGrid>
Explanation
RelativeBinding works due to the fact that DataGridCells are eventual children of DataGridRows as can been seen in this screenshot of the visual tree of a DataGrid:

Selecting a partially displayed WPF checkbox

I am having a design problem and I know there has to be a way to make it work. I tried the solutions here: Annoying auto scroll of partially displayed items in WPF ListView
But they didnt work for me because I am not allowed to work in the code-behind.
I have a list of items from a wpf ListBox. like this:
when I try to select the CheckBox in line 5, the Window centers on it but does not check it. After further testing, I found that it will not select the CheckBox as long as the bottom border of the item is not in view.
Here is the xaml for the ListBox and its Style:
<ListBox Grid.Column="0" Grid.Row="1" Name="RequestCheckoutV"
ItemsSource="{Binding Path=CheckoutVM, Mode=TwoWay, IsAsync=True}"
SelectedItem="{Binding Path=SelectedPermit}"
BorderThickness="0"
KeyboardNavigation.TabNavigation="Continue">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="KeyboardNavigation.IsTabStop" Value="False" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Control.HorizontalContentAlignment" Value="Center"/>
<Setter Property="Control.VerticalContentAlignment" Value="Top"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}" >
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
What can I do to make this select the checkbox instead of just centering it?
Any help is appreciated.
So by adding the specification of "Press" to ClickMode property to CheckBox via ClickMode="Press" you're telling that object to basically ignore the event otherwise hijacked by the list item template. Now it will accept the first event in it's hit area for itself instead of the list item since it's default is "Release". Cheers :)
Try adding to your .ItemContainerStyle:
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
This will effectively give focus to anything you click in there.

ListView Change background of single item

Let's assume I have the following listview:
<ListView Name="list" />
and the following code inside my function:
list.Items.Add("red");
list.Items.Add("green");
How can I change the background of the first item to red and second one to green?
You can make use of ItemContainerStyle to define a style for the items.
In the style you can bind the Background property to the item itself which is representation of the color.
Now the implicit color converter will convert to appropriate color and set to the background property.
example
<ListView Name="list" >
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Background"
Value="{Binding}" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
result
Alternate approch
here is how you can use Triggers to set the values conditionally
I used DataTrigger to see if the item meet my condition and then I can set the desired color via a setter
<ListView Name="list">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Style.Triggers>
<DataTrigger Binding="{Binding}"
Value="red">
<Setter Property="Background"
Value="red" />
</DataTrigger>
<DataTrigger Binding="{Binding}"
Value="mygreen">
<Setter Property="Background"
Value="green" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
</ListView>
result
in above example you can see that the background color is only applied to only desired items eg. red & mygreen

Trouble setting a DataTrigger in WPF

I have a ComboBox and a Button on my main view, and I want to apply a style to the button such that when the combobox index is set to 1, the button becomes visible (initially it's hidden). This is my XAML code:
<Grid>
<StackPanel Orientation="Vertical" Margin="10">
<ComboBox Name="comboBox"/>
<Button Name="myBtn" Content="Hello" Visibility="Hidden">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=comboBox, Path=SelectedIndex}" Value="1">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
</Grid>
Someone already asked a question about this here, and I'm doing pretty much the same thing, but it doesn't work, the button remains hidden even when the index is changed to 1. The comobox is initially being populated in the code behind with 2 items. Any help is appreciated.
The problem is that dependency property values set locally (like you've done with visibility) have a higher precedence then those set from a style trigger. As such, even when the trigger is hit, it won't override the value you've already set.
The simple solution is to instead set the default value in a style Setter:
<Button Name="myBtn" Content="Hello">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Visibility" Value="Hidden"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=comboBox, Path=SelectedIndex}" Value="1">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
And now your trigger will override the property value when it is hit.
While you're at it, you should have a look at this link that lists the precedence order for setting DP values.

DataGridTemplateColumn.HeaderTemplate issue

Please help on this issue and I'm not sure how to handle this.
I've combox box and datagrid. When ever I've selected combox value datagrid should be loaded with new data and that works perfectly. But I've DataGridTemplateColumn.HeaderTemplate with checkbox when I checked all the column with checkbox is also checked and also worked with unchecked as well. Both are fine.
Now my issue is when i selected combobox, datagrid --> headertemplate checkbox should be unchecked. IS there any event I can fire for this? My code below.
Combox is outside the datagrid.
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Name="checkadded" Margin="6,0" IsChecked="{Binding IsSelected, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<CheckBox Name="ChkAllAdd" IsChecked="False" Width="50" Loaded="chkallLoaded" Checked="ChkAll_Checked" Unchecked="ChkAll_Unchecked" IsThreeState="False" Padding="4,3,4,3" HorizontalContentAlignment="Left" HorizontalAlignment="Center" />
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Foreground" Value="#686868"/>
<Setter Property="FontWeight" Value="Bold" />
</Style>
<Style TargetType="{x:Type DataGridCell}" >
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="white"/>
<Setter Property="Background" Value="#93A8A9"/>
<Setter Property="FontWeight" Value="Bold"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
</DataGrid>
Create a ControlTemplate for Content Control and place your DataGrid and ComboBox Xaml in that. Now you can create an EventTrigger for Combobox and there you can set the CheckedBox Checked using element name in binding. This can be helpfull.

Categories