Dynamic content of a Grid - c#

My data model has a property of the enumeration type. I wonder if there is way to place dynamically a user control based on the value of the enumeration type?
I am currently investigating in the following direction:
<Grid Name ="AdjustmentsArea" DockPanel.Dock ="Right" MinWidth ="100" Visibility ="Collapsed" >
<ContentControl DataContext ="{Binding AjustmentView}">
<Style TargetType ="model:AjustmentViews">
<Style.Triggers>
<DataTrigger Binding ="{Binding}" Value ="Settings">
/// is it possible in principle to point a user control using a Setter ???
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl>
</Grid>
May be also I am on a wrong path. But I would like to know (learn) if it is possible to implement this requirement for dynamic content in user control, but not using hide/show exised element approach.
What would you recommend?

you can set different template depending on trigger binding value
<ContentControl DataContext ="{Binding AjustmentView}">
<ContentControl.Style>
<Style TargetType ="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding}" Value ="Settings">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate> <!--template with UserControl here--> </ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
e.g. WPF Slider uses this approach when Orientation changes (Horizontal or Vertical)

Related

Lost previous combobox item style after applying style for cursor on hover

I want all clickable elements in the app to have hand cursor on hover. My problem is in comboboxes: all of them apply default (or what?) style when I set my custom style below (but hand cursor works!):
<Style x:Key="ComboBoxItemStyle" TargetType="ComboBoxItem">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Hand"/>
</Trigger>
</Style.Triggers>
</Style>
<StackPanel Grid.Column="1" HorizontalAlignment="Right" Orientation="Horizontal">
<TextBlock Style="{StaticResource OptionTextBlockStyle}" />
<ComboBox x:Name="SortTypesComboBox" FontSize="14px" DisplayMemberPath="Description" ItemContainerStyle="{StaticResource ComboBoxItemStyle}">
</ComboBox>
</StackPanel>
The first image is what I want but with hand cursor on clickable items, the second one is what I actually have applying my comboboxitemstyle.
I don't know why I lost my previous style using this line ItemContainerStyle="{StaticResource ComboBoxItemStyle}. How can I save it and make right cursor on hover at the same time?
---UPG----
I've just added BasedOn="{StaticResource {x:Type ComboBoxItem}}" line and this didn't help me much. Now I have my items with wrong style and green lightning:
The way that you define your style overrides the default style for ComboBox. Instead, you should base your style on the default style using the BasedOn property.
<Style x:Key="ComboBoxItemStyle"
TargetType="ComboBoxItem"
BasedOn="{StaticResource {x:Type ComboBoxItem}}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Hand"/>
</Trigger>
</Style.Triggers>
</Style>
The reason for referencing the default style with {x:Type ComboBoxItem} is that it is an implicit style, which gets applied automatically to each control of that type in scope unless a different style is assigned explicitly and the x:Key of an implicit style is their TargetType.

WPF DataGrid Custom Row Header

I am working on a DB FrontEnd with WPF / EntityFramework / MVVM
Now i got stuck when i allow the user to add data to a datagrid (which is bound to an Observable Collection).
What i want to achieve, is to get a row header like in MS Access:
So my WPF DataGrid should look like this basically:
Is there any way to bind the RowHeaderStyle to the RowState?
For Example:
RowState.Editing: Show Edit Icon
RowState.NewRow: Show Star
RowState.Default: Show Default Row Header
I found no solution so far, but i think WPF should pe powerfull enough to get this job done.
Thank you!
Simple. Give the DataGrid a RowHeaderStyle that swaps in different ContentTemplates depending on the state of the DataGridRow. Fortunately the DataGridRow is a visual ancestor of the DataGridRowHeader, so it's relatively simple to reach up there with a RelativeSource binding and get the values of the relevant properties: DataGridRow.IsEditing and DataGridRow.IsNewItem.
I used <Label>New</Label> etc. as an arbitrary stand-in for whatever content you want to use.
<DataGrid
ItemsSource="{Binding Rows}"
>
<DataGrid.RowHeaderStyle>
<Style
TargetType="{x:Type DataGridRowHeader}"
BasedOn="{StaticResource {x:Type DataGridRowHeader}}"
>
<!--
Empty content template for default state.
Triggers below replace this for IsNewItem or IsEditing.
-->
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Label></Label>
</DataTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger
Binding="{Binding
IsEditing,
RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}"
Value="True"
>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Label>Edit</Label>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger
Binding="{Binding
IsNewItem,
RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}"
Value="True"
>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Label>New</Label>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowHeaderStyle>
</DataGrid>

Set property in ContentPresenter child control

I want to set TextTrimming on TextBlock.
<Style TargetType="{x:Type dg.CellValuePresenter}">
<Setter Property="ContentTemplate" Value="{StaticResource Tmp}" />
</Style>
My template:
<DataTemplate x:Key="Tmp">
<ContentPresenter Content="{Binding}" >
<ContentPresenter.Resource>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="TextTrimming" Value="CharacterEllipsis"/>
</Style>
</ContentPresenter.Resource>
</ContentPresenter>
</DataTemplate>
Content Presenter is XamTextEditor from Infragistic:
<Style TargetType="{x:Type igEditors:XamTextEditor}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type igEditors:XamTextEditor}">
<TextBlock/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Setter from DataTemplate doesn't work. Do you know how to set this property?
How can I set TextTrimming property in Textblock of XamTextEditor cudtom style? I cannot do this in this style because it is used in other datatemplates where texttrimming must be turn off. So I tried to set it in DataTemplate but it not work (i see in snoop that it is set to none)
Unfortunately I cannot show more code because I don't have access to internet on dev and write down more code is time consuming :/
I'm not sure if I get your problem completely.
But If you are using explicit column definition. Then instead of CellvaluePresenter try create a EditorStyle For any column type(I've done it for string type column so the editor would be XamTextEditor)
<!--(xmlns:igWindows="http://infragistics.com/Windows")-->
<Style TargetType="{x:Type igEditors:XamTextEditor}" x:Key="DefaultXamDateTimeEditor">
<Style.Resources >
<Style TargetType="{x:Type igWindows:SimpleTextBlock}" >
<Setter Property="TextTrimming" Value="CharacterEllipsis" />
</Style>
</Style.Resources>
</Style>
And use this style as columns EditorStyle. It should work in XamDatagrid as this is working for me. Please post specific situation If I didn't get the question right.

Using BasedOn and Trigger to manipulate nested property in parent Style

We have a file of standard styles. One style, SectionGroup, we use on all our GroupBox elements. It has a custom template which uses a Border to put an underline below the header, among other things.
On one page, we have a checkbox next to a GroupBox header; when the user unchecks the checkbox, the contents of the GroupBox hide (visibility collapsed) and the header remains. Unfortunately the underline under the header then looks ugly; we also want to hide this.
I've given it my best attempt, so the parent SectionGroup style now looks like this:
<Style x:Key="SectionGroup" TargetType="GroupBox">
<Style.Resources>
<Thickness x:Key="HeaderBorderThickness">0,0,0,1</Thickness>
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupBox">
<Grid Margin="0">
...
<Border Grid.Row="0" BorderThickness="{DynamicResource HeaderBorderThickness}" >
<TextBlock Text="{Binding Header, RelativeSource={RelativeSource AncestorType=GroupBox}}"/>
</Border>
...
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
By defining the HeaderBorderThickness resource and using it as a DynamicResource, I can override it in my page (as explained in this answer):
<GroupBox>
<GroupBox.Style>
<Style TargetType="GroupBox" BasedOn="{StaticResource SectionGroup}">
<Style.Resources>
<Thickness x:Key="HeaderBorderThickness">0,0,0,0</Thickness>
</Style.Resources>
<!-- TODO triggers here.. -->
</Style>
</GroupBox.Style>
<GroupBox.Header>Section One</GroupBox.Header>
...contents...
</GroupBox>
So indeed, by redefining a Thickness of the same key, the DynamicResource works as expected and there is no underline on the header.
Now I need to toggle it based on a trigger/binding. I'm pretty new to this, but elsewhere in this page I have figured out to do stuff like this:
<Grid Visibility="{Binding Path=FooBoolean, Converter={StaticResource BooleanToVisibility}}">
I think there's a little more magic involved in our viewmodel class (followed the example of existing bindings & properties), but it works.
Now the question is -- how do I bind the boolean value in FooBoolean, to the HeaderBorderThickness resource value? Or what other means can I use to accomplish my goal?
It seems to me that you could do this in a much more WPF way with a DataTrigger, perhaps something like this:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Header, RelativeSource={RelativeSource AncestorType=
GroupBox}}"/>
<Rectangle Grid.Row="1" Height="1" Margin="0,2,0,0" Fill="Black">
<Rectangle.Style>
<Style>
<Setter Property="Rectangle.Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{IsChecked, ElementName=YourCheckBox}"
Value="False">
<Setter Property="Rectangle.Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
</Grid>
This method enables you to set the Width, Height, Padding and whatever other properties of the line. If you can't access the CheckBox directly from the Style, then you could try adding a bool property to bind to both the Checkbox.IsChecked property and the DataTrigger.Binding property. Or just manage the Rectangle.Visibility in your own method.

How to switch between views using DataTemplate + Triggers

I have a requirement where a where user can switch to view hierarchical data either as tree or as a text in datagrid or as FlowChart.
The user can do this by clicking a Toggle Button which say: Switch Mode. I want to do all this in such a way that it can be handled within the View only as ViewModel in all the three cases is the same.
How do I apply View to my ViewModel based on Trigger.
If the state of which view to show is saved in some enum property you could use a ContentControl and DataTriggers for example:
<ContentControl>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding ViewMode}" Value="TreeMode">
<Setter Property="Content">
<Setter.Value>
<uc:TreeModeView />
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding ViewMode}" Value="GridMode">
<Setter Property="Content">
<Setter.Value>
<uc:GridModeView />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
(As the style is only used in one place, by setting it directly as ContentControl.Style this will work, if you want to use it in more than one place you should set the ContentTemplate instead, because otherwise there will only be one view instance shared by all controls with the style which is not allowed by WPF (of course Content needs to be set to something for the template to be applied))
You could also bind directly to IsChecked of the ToggleButton using ElementName of course. The relevant values would then be True, False and {x:Null}.
H.B.'s answer is good, but there are scenarios where it's not quite so good.
If constructing the views (and their underlying view models) is expensive, then toggling the Content property will pay this expense every time the user changes the view.
In some scenarios, it makes sense to create both views in the same container (e.g. a Grid), and toggle their Visibility instead. If you use lazy evaluation in your view models, the expensive operations won't be conducted until the view becomes visible, and - importantly - they'll only be conducted the first time the view becomes visible. Once both views have been displayed, the user can toggle back and forth between views without reconstructing the underlying view models.
Edit:
I stand corrected, sort of: H.B.'s answer is not as good as it looked .
You can't use a style to set the Content property of a ContentControl to a UIElement. See this blog post for full details, but the long and short of it is that if you use H.B.'s approach, you'll get a runtime error.
You can set the ContentTemplate property, instead, e.g.:
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding ViewMode}"
Value="TreeMode">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<uc:TreeModeView/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding ViewMode}"
Value="GridMode">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<uc:GridModeView/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>

Categories