I have my own custom combobox with last item as button with command:
Code:
public class CustomComboBox : ComboBox
{
public static readonly DependencyProperty ButtonCommandProperty =
DependencyProperty.Register("ButtonCommand", typeof (ICommand), typeof (CustomComboBox), null);
public ICommand ButtonCommand
{
get { return (ICommand) GetValue(ButtonCommandProperty); }
set { SetValue(ButtonCommandProperty, value); }
}
}
Styles:
<Style TargetType="{x:Type controls:CustomComboBox}">
<Style.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="Height" Value="16" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<TextBlock Text="{TemplateBinding Content}" Background="Transparent" Foreground="#CCCCCD" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Style.Resources>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:CustomComboBox}">
<StackPanel>
<ComboBox
SelectedItem="{Binding Path=SelectedItem, Mode=TwoWay,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type controls:CustomComboBox}},
UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding ItemsSource, Mode=OneWay,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type controls:CustomComboBox}},
UpdateSourceTrigger=PropertyChanged}">
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel>
<TextBlock Style="{StaticResource TextBlockDimmed}" Text="{Binding Mode=OneWay}" />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger
Binding="{Binding Converter={converters:ComboBoxItemIndexToStringConverter},
RelativeSource={RelativeSource Self}}" Value="IsLastItem">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel>
<Button Content="{Binding}"
Command="{Binding ButtonCommand,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type controls:CustomComboBox}}}" />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Using:
<controls:CustomComboBox Grid.Column="1"
SelectedItem="{Binding Path=SelectedAreaName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ButtonCommand="{Binding CreateNewAreaCommand}"
ItemsSource="{Binding AreasNamesList, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
When I try to call button command in programm (click on the button) more than once then I got an error message:
'Specified element is already the logical child of another element. Disconnect it first.'
I guess the problem in XAML styles, but i'm not sure in what the problem exaclty is.
Related
Is it possible to "select" a different control template based on a property of a viewmodel?
I have the following user control template:
<UserControl.Template>
<ControlTemplate TargetType="UserControl">
<RadioButton
GroupName="DisplayButtons"
Content="{TemplateBinding Content}"/>
</ControlTemplate>
</UserControl.Template>
Based on a boolean in the viewmodel, I want to use either a RadioButton or a Button.
How can I achieve that?
You could use a Style with a DataTrigger that sets the Template property:
<UserControl>
<UserControl.Style>
<Style TargetType="UserControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="UserControl">
<Button Content="Button" />
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding ViewModelProperty}" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="UserControl">
<RadioButton Content="RadioButton" />
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Style>
</UserControl>
It is possible to use different views for each case but this method is more useful in the case of complicated templates.
For the current case, the easiest way is to use Triggers:
Method 1
<UserControl x:Class="Myusercontrolnamespace.Views.Myusercontrol"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d = "http://schemas.microsoft.com/expression/blend/2008" "
mc:Ignorable = "d" Height="auto" Width="auto" >
<UserControl.Resources>
<DataTemplate x:Key="RadioButtontTemplate">
<RadioButton/>
</DataTemplate>
<DataTemplate x:Key="ButtonTemplate">
<Button/>
</DataTemplate>
</UserControl.Resources>
<Grid>
<ContentControl Content="{Binding }">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource RadioButtontTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsRadioButton}" Value="False">
<Setter Property="ContentTemplate" Value="{StaticResource ButtonTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</Grid>
</UserControl>
Method 2
<UserControl x:Class="Myusercontrolnamespace.Views.Myusercontrol"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d = "http://schemas.microsoft.com/expression/blend/2008" "
mc:Ignorable = "d" Height="auto" Width="auto" >
<UserControl.Resources>
<DataTemplate x:Key="RadioButtontTemplate">
<RadioButton/>
</DataTemplate>
<DataTemplate x:Key="ButtonTemplate">
<Button/>
</DataTemplate>
</UserControl.Resources>
<Grid>
<DataTemplate x:Key="globalControlTemplate">
<ContentControl Content="{Binding }">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource RadioButtontTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding ConsumerType}" Value="Business">
<Setter Property="ContentTemplate" Value="{StaticResource ButtonTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
<ContentControl Content="{Binding globalControlTemplate}" />
</Grid>
</UserControl>
Cordially
I have the following WPF ListView where I change the template depending on the property ChangeView in my class MyItemsClass.
<ListView x:Name="MyListView" ItemsSource="{Binding}">
<ListView.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type myClass:MyItemsClass}}, Path=ChangeView}" Value="True">
<Setter Property="ListView.ItemsPanel" Value="{StaticResource largeTemplate}"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type myClass:MyItemsClass}}, Path=ChangeView}" Value="False">
<Setter Property="ListView.ItemsPanel" Value="{StaticResource smallTemplate }" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.Style>
<ItemsPanelTemplate x:Key="largeTemplate">
<WrapPanel Orientation="Horizontal" IsItemsHost="True">
<WrapPanel.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisibility"/>
<HierarchicalDataTemplate DataType="{x:Type local:MyData}">
<Button Command="{Binding}" Visibility="{Binding RelativeSource={RelativeSource Self}, Path=IsEnabled, Converter={StaticResource BoolToVisibility}}">
<Image Height="100" Width="100" Source="{Binding Path=MyImage}"/>
</Button>
</HierarchicalDataTemplate>
</WrapPanel.Resources>
</WrapPanel>
</ItemsPanelTemplate>
smallTemplate is the same as largeTemplate XAML only with different sizes of iamge.
Now I want to 'skin' my ListView with an existing style of ListView so if I do the following it works
<ListView x:Name="MyListView" ItemsSource="{Binding}" ItemContainerStyle="{DynamicResource MyStyle}">
</ListView>
<Style x:Key="MyStyle" TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource ListViewItemStyle}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
So now I am attempting to add in the original triggers like so:
<ListView x:Name="MyListView" ItemsSource="{Binding}" ItemContainerStyle="{DynamicResource MyStyle}">
</ListView>
<Style x:Key="MyStyle" TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource ListViewItemStyle}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type myClass:MyItemsClass}}, Path=ChangeView}" Value="True">
<Setter Property="ListView.ItemsPanel" Value="{StaticResource largeTemplate}"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type myClass:MyItemsClass}}, Path=ChangeView}" Value="False">
<Setter Property="ListView.ItemsPanel" Value="{StaticResource smallTemplate }" />
</DataTrigger>
</Style.Triggers>
</Style>
But now my DataTriggers do not work - the ListView.ItemsPanel does not change when my class variable is changed. How canI get this to work?
Try this instead:
<ListView x:Name="MyListView" ItemsSource="{Binding}" Style="{DynamicResource MyStyle}">
</ListView>
<Style x:Key="MyStyle" TargetType="{x:Type ListView}" BasedOn="{StaticResource ListViewStyle}">
<Setter Property="ItemTemplate">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type myClass:MyItemsClass}}, Path=ChangeView}" Value="True">
<Setter Property="ListView.ItemsPanel" Value="{StaticResource largeTemplate}"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type myClass:MyItemsClass}}, Path=ChangeView}" Value="False">
<Setter Property="ListView.ItemsPanel" Value="{StaticResource smallTemplate }" />
</DataTrigger>
</Style.Triggers>
</Style>
I changed your style to target the Style of the ListView itself, rather than its ItemContainerStyle property, and then I changed <Setter Property="Template"> to <Setter Property="ItemTemplate"> so that it will only override the template for your items, rather then overriding the template for the whole ListView.
So you have a working ListView style? Why don't you simply set the ItemContainerStyle property of it to your "MyStyle"?:
<ListView x:Name="MyListView" ItemsSource="{Binding}" ItemContainerStyle="{DynamicResource MyStyle}">
<ListView.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type myClass:MyItemsClass}}, Path=ChangeView}" Value="True">
<Setter Property="ListView.ItemsPanel" Value="{StaticResource largeTemplate}"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type myClass:MyItemsClass}}, Path=ChangeView}" Value="False">
<Setter Property="ListView.ItemsPanel" Value="{StaticResource smallTemplate }" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.Style>
</ListView>
MyStyle applies to a ListViewItem and not to a ListView so this should work.
Or what is the "skin" that you are trying to apply?
I have a list box with an ItemContainerStyle which describes the style of each element in the listbox. Like looks something like this:
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem" BasedOn="{StaticResource MyStyle}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderThickness="0,0,0,1" BorderBrush="#1f000000" Padding="16 8">
<Button Command={Binding MyCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=lists:MyControl}}}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
The thing is that, when I click this button I want to know the SelectedItem in the listbox, which is bound in my ViewModel. This selection doesnt trigger unless I select the item first.
Any Ideas?
You need to force the IsSelected with a trigger:
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True"/>
</Trigger>
</Style.Triggers>
Here is a full working example:
<ListBox x:Name="ListBox" ItemsSource="{Binding SomeList}" SelectedItem="{Binding SelectedListElement, Mode=TwoWay}" IsSynchronizedWithCurrentItem="True" >
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem" >
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True"/>
</Trigger>
</Style.Triggers>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderThickness="0,0,0,1" BorderBrush="#1f000000" Padding="16 8">
<Button Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBox}},
Path=DataContext.Run}" CommandParameter="{Binding}" Height="30" Width="100"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
Could someone help with this.
Im trying to make the following code so that i can inject what ever image i want into this button style. as this button style is used throughout the whole application
<Style TargetType="Button" x:Key="ButtonIsChecked">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Focusable" Value="False"/>
<Setter Property="Margin" Value="5"/>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<CheckBox Content="{Binding Content}" Name="CheckBox_Logon" IsHitTestVisible="False" IsChecked="False">
<CheckBox.Template>
<ControlTemplate TargetType="{x:Type CheckBox}">
<WrapPanel>
<Image
Source="/AdminUltimate;component/Images/Icons/Windows.ico"
Width="15" Margin="3"
Visibility="{Binding IsChecked, Converter={StaticResource BoolToVis}, ElementName=DisableIcons}"/>
<TextBlock Text="{Binding}" VerticalAlignment="Center"/>
</WrapPanel>
</ControlTemplate>
</CheckBox.Template>
</CheckBox>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Current Usage
<Button Content="Login" Style="{StaticResource ButtonIsChecked}"/>
i would like to do something like
<Button Content="Login" Style="{StaticResource ButtonIsChecked}" imgsrc="Pathtoimage"/>
Is this possible?
Something quick and easy to use to piggy back a dependency string through is Tag like;
<Style TargetType="Button" x:Key="ButtonIsChecked">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Focusable" Value="False"/>
<Setter Property="Margin" Value="5"/>
<Setter Property="Tag" Value="/Default/Image/Path.jpg"/>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<CheckBox Content="{Binding Content}" Name="CheckBox_Logon" IsHitTestVisible="False" IsChecked="False">
<CheckBox.Template>
<ControlTemplate TargetType="{x:Type CheckBox}">
<WrapPanel>
<Image
Source="{TemplateBinding Tag}"
Width="15" Margin="3"
Visibility="{Binding IsChecked, Converter={StaticResource BoolToVis}, ElementName=DisableIcons}"/>
<TextBlock Text="{Binding}" VerticalAlignment="Center"/>
</WrapPanel>
</ControlTemplate>
</CheckBox.Template>
</CheckBox>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Then implement like;
<Button Style="{StaticResource ButtonIsChecked}" Tag="/AdminUltimate;component/Images/Icons/Windows.ico"/>
Hope this helps.
I'm creating a new control for my WPF application. In it, I have added two dependency properties. These ones is to give style a listbox with a bullet decorator.
Here is what I have:
public class QuestionControl : Control
{
static QuestionControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(QuestionControl), new FrameworkPropertyMetadata(typeof(QuestionControl)));
}
// Another dependency properties...
// PossibleAnswers : IEnumerable
public IEnumerable PossibleAnswers
{
get { return (IEnumerable)base.GetValue(PossibleAnswersProperty); }
set { base.SetValue(PossibleAnswersProperty, value); }
}
public static readonly DependencyProperty PossibleAnswersProperty =
DependencyProperty.Register("PossibleAnswers", typeof(IEnumerable), typeof(QuestionControl));
// PossibleAnswers : DataTemplate
public DataTemplate PossibleAnswersTemplate
{
get { return (DataTemplate)base.GetValue(PossibleAnswersTemplateProperty); }
set { base.SetValue(PossibleAnswersTemplateProperty, value); }
}
public static readonly DependencyProperty PossibleAnswersTemplateProperty =
DependencyProperty.Register("PossibleAnswersTemplate", typeof(DataTemplate), typeof(QuestionControl));
}
Then I've got the generic style here. Please, watch the last style I've set the PossibleAnswers to the listbox and the binding is okay. But at the momment to pass the data template (this one is setted to the content control called ContentText) and watch I've set the template called PossibleAnswersTemplate and it's wrong at this part.
<ContentControl x:Name="ContentText" Margin="2 0 0 0" Content="{Binding}" ContentTemplate="{TemplateBinding PossibleAnswersTemplate}" />
Does not compile. What should be the error?
<Style TargetType="ListBox" x:Key="KinectRadioList">
<Style.Setters>
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type ListBoxItem}">
<Style.Setters>
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<ControlTemplate.Resources>
<Style TargetType="{x:Type Ellipse}">
<Style.Setters>
<Setter Property="Fill" Value="Black"/>
<Setter Property="Stroke" Value="Black"/>
</Style.Setters>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={x:Static RelativeSource.TemplatedParent}, Path=IsEnabled}" Value="False">
<Setter Property="Fill" Value="Black"/>
<Setter Property="Stroke" Value="Black"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ControlTemplate.Resources>
<BulletDecorator Background="Transparent" >
<BulletDecorator.Bullet>
<Canvas Width="15" Height="15">
<Ellipse Width="13" Height="13" Canvas.Left="1" Canvas.Top="1" StrokeThickness="1" Fill="{x:Null}"/>
<Ellipse Width="8" Height="8" Canvas.Left="3.5" Canvas.Top="3.5" Stroke="{x:Null}" Visibility="{Binding RelativeSource={x:Static RelativeSource.TemplatedParent}, Path=IsSelected, Converter={StaticResource BoolToVisibilityConverter}}"/>
</Canvas>
</BulletDecorator.Bullet>
<ContentControl x:Name="ContentText" Margin="2 0 0 0" Content="{Binding}" ContentTemplate="{TemplateBinding PossibleAnswersTemplate}" />
</BulletDecorator>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
<Style TargetType="{x:Type local:QuestionControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:QuestionControl}">
<StackPanel>
<ContentControl Content="{TemplateBinding QuestionText}"
ContentTemplate="{TemplateBinding QuestionTextTemplate}" />
<ListBox ItemsSource="{TemplateBinding PossibleAnswers}" SelectionMode="Multiple"
Style="{StaticResource KinectRadioList}"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ListBox ItemsSource="{TemplateBinding PossibleAnswers}" Tag="{TemplateBinding PossibleAnswersTemplate}" SelectionMode="Multiple"
Style="{StaticResource KinectRadioList}"/>
<ContentControl ContentTemplate="{TemplateBinding Tag}"/>
Your Template is of ListBox and TemplateBinding is Trying to find PossibleAnswersTemplate in ListBox and hence it is not working. I hope this will help.
EDIT: This is another solution
<ContentControl Margin="2 0 0 0" Content="{Binding}" ContentTemplate="{Binding PossibleAnswersTemplate, RelativeSource={RelativeSource AncestorType={x:Type local:QuestionControl}}}" />