I am trying to bind a a Style-Resource which is defined in the local ResourceDictionary of a UserControl to all items in a StackPanel.
The ItemSource of the StackPanel is bound to an ObservableCollection<Button> in the ViewModel.
Thus, the aim is to bind the Style-Resource to the Style-Property of these Buttons.
The following simplified approach results in this error:
ArgumentException: Style object is not allowed to affect the Style property of the object to which it applies.
MyViewModel.cs:
public class MyViewModel
{
public ObservableCollection<Button> MyButtons { get; private set; }
}
MyView.xaml
<UserControl x:Class="MyView"
d:DataContext="{d:DesignInstance Type=MyViewModel}">
<UserControl.Resources>
<ResourceDictionary>
<Style x:Key="StyleStackPanelButton" TargetType="{x:Type Button}"
BasedOn="{StaticResource MyDefaultStyle}">
<Setter Property="Margin" Value="15" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</ResourceDictionary>
</UserControl.Resources>
<StackPanel>
<ItemsControl ItemsSource="{Binding MyButtons}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Style" Value="{StaticResource StyleStackPanelButton}" />
</Style>
</StackPanel.Resources>
</StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<Button Style="{StaticResource StyleStackPanelButton}" />
</StackPanel>
</UserControl>
I've also tried to use a Converter as suggested here
Binding for WPF Styles
as follows:
...
<ItemsControl ItemsSource="{Binding ButtonExtensions}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Style">
<Setter.Value>
<MultiBinding Converter="{StaticResource StyleConverter}">
<MultiBinding.Bindings>
<Binding RelativeSource="{RelativeSource Self}" />
<Binding Source="{StaticResource StyleStackPanelButton}" />
</MultiBinding.Bindings>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Resources>
</StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
...
This results in the same error:
ArgumentException: Style object is not allowed to affect the Style property of the object to which it applies.
Is there a any other way to bind a lcoal Style-Resource to a Setter-Property of an Item in a Stackpanel-ItemSource?
Since you are adding Buttons directly to the ItemsSource collection, it is sufficient to assign the Button Style to the ItemContainerStyle property of the ItemsControl:
<ItemsControl ItemsSource="{Binding MyButtons}"
ItemContainerStyle="{StaticResource StyleStackPanelButton}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Alternatively, declare a default Button Style - without x:Key - in the StackPanel Resources:
<ItemsControl ItemsSource="{Binding MyButtons}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="Button"
BasedOn="{StaticResource StyleStackPanelButton}"/>
</StackPanel.Resources>
</StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Related
I'm new on WPF and I'm tryng to figure out how to implement a binding with different type of user control.
After the user clicks a button, a usercontrol (a simple shape like rectangle or ellipse) is added to the window.
I'm tryng to use an MVVM approach so the xaml appears as follow:
...
Title="{Binding Path=Titolo}"
Height="450" Width="800"
d:DataContext="{d:DesignInstance vm:MainWindowViewModel}">
<Canvas>
<Button Content="Add Shape" Command="{Binding TestCommand}"/>
<ItemsControl ItemsSource="{Binding RectCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<uc:MyCustomRectangle/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding xLT}" />
<Setter Property="Canvas.Top" Value="{Binding yLT}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Canvas>
All works fine but I want to add different type of UserControl (not only MyCustomRectangle) using the same button (for example, randomly adding rectangle or ellipse).
A possible solution could be duplicate the section of ItemsControl and select a different collection of binding:
<Canvas>
<Button Content="Add Shape" Command="{Binding TestCommand}"/>
<ItemsControl ItemsSource="{Binding RectCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<uc:MyCustomRectangle/> <!-- bind to my usercontrol -->
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding xLT}" />
<Setter Property="Canvas.Top" Value="{Binding yLT}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
<ItemsControl ItemsSource="{Binding EllipseCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<uc:MyCustomEllipse/> <!-- bind to my usercontrol -->
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding xLT}" />
<Setter Property="Canvas.Top" Value="{Binding yLT}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Canvas>
I don't think this is the correct solution, especially because I would to add many different types of shapes (and also text and images).
So, if exist, what is the correct way to bind a data template to different type of usercontrol?
Is MVVM the correct approach to solve this problem?
What you can do is to bind to a global list with all Shapes.
And then you can define different DataTemplates for different Types.
Like this:
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type MyType1}">
...
</DataTemplate>
<DataTemplate DataType="{x:Type MyType2}">
....
</DataTemplate>
</ItemsControl.Resources>
I define a style to make all StackPanel green:
<Window.Resources>
<Style TargetType="StackPanel">
<Setter Property="Background" Value="Green" />
</Style>
</Window.Resources>
But if I use StackPanel as panel template then it's NOT green:
<UniformGrid>
<StackPanel /><!-- this one is green -->
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel /><!-- this one is not -->
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</UniformGrid>
Why? How to make it also green?
Either move the implicit Style to App.xaml or add resource that is based on the implicit Style to the ItemsPanelTemplate:
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<ItemsPanelTemplate.Resources>
<Style TargetType="StackPanel" BasedOn="{StaticResource {x:Type StackPanel}}" />
</ItemsPanelTemplate.Resources>
<StackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Types that don't inherit from Control won't pick up implicit styles if you don't do any of this.
I'm trying to group a listbox. And all I can get to show up is the header.
I've got a list list of 'Online Users' , which look like this.
public class OnlineUser{
public string Branch {get;set;}
public string FirstName{get;set;}
public string LastName{get;set;}
}
Then i populate the list with some users, and put that list into a ICollectionView 'FilterableOnlineUsers'
FilterableOnlineUsers = CollectionViewSource.GetDefaultView(OnlineUsers);
FilterableOnlineUsers.GroupDescriptions.Add(new PropertyGroupDescription("Branch"));
FilterableOnlineUsers.SortDescriptions.Add(new SortDescription("Branch", ListSortDirection.Descending));
And in my Xaml:
<ListBox SelectedItem="{Binding DataContext.SelectedUser" ItemsSource="{Binding DataContext.FilterableOnlineUsers" >
<ListBox.GroupStyle>
<GroupStyle />
</ListBox.GroupStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} {1}">
<Binding Path="FirstName"></Binding>
<Binding Path="LastName"></Binding>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
All I can get to show up in the Listbox is the branch name. I can't get first name or lastname to show up underneath the group Descriptions..
Thanks.
You should define a CollectionViewSource in XAML resource Like Below and make the ItemsSource set to the CollectionViewSource,
<CollectionViewSource x:Key="ListBoxItems" Source="{Binding Path=ListOfOnlineUser}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Branch" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
In List Box
<ListBox ItemsSource="{Binding Source={StaticResource ListBoxItems}}"/>
Detail
Below is the ListBox show grouped branch and each branch is inside an expander which you can collapse and expand each groups.
<ListBox
Margin="0,0,5,0"
ItemsSource="{Binding Source={StaticResource ListBoxItems}}"
SelectedIndex="-1"
SelectedItem="{Binding SelectedBranch}">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</GroupStyle.Panel>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander
Padding="0"
BorderThickness="0"
Header="{Binding Name}"
IsExpanded="True">
<ItemsPresenter/>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListBox.GroupStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderThickness="0">
<Grid>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FirstName}" />
<TextBlock Text="{Binding LastName}" />
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="0" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
I have a WPF Prism project and it has a Region base on ItemControl:
<ItemsControl prism:RegionManager.RegionName="WorkspaceRegion" >
In this ItemControl i see some of my Views verticaly well but i want give a style to each Item of ItemControl (each View).
All of items (views) must have same style (for example: background
color, padding, margin, border and...)
I want something like this (for example):
I used a simple style and code like this:
<ItemsControl prism:RegionManager.RegionName="WorkspaceRegion" Background="#765e4d" Margin="10">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="Red" Padding="10" BorderThickness="1" CornerRadius="5">
<ContentPresenter Content="{Binding}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
Its Error:
A style intended for type 'ItemsControl' cannot be applied to type
'View1'
Also i tested this codes:
<ItemsControl prism:RegionManager.RegionName="WorkspaceRegion" Background="#765e4d" Margin="10">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid >
<TextBlock Text="Test"/>
<Border BorderBrush="Red" Padding="10" Margin="10" BorderThickness="1" CornerRadius="5">
<ContentPresenter Content="{Binding}"/>
</Border>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
But the result is like when i write:
<ItemsControl prism:RegionManager.RegionName="WorkspaceRegion" >
Why? What is my mistake?
How can i do this?
Edit1:
I used <ItemsPresenter/> instead of <ContentPresenter Content="{Binding}"/>
Result: without any change
Edit2:
I write this style for ItemContainerStyle property of the ItemsControl and it works if i remove ControlTemplate part from it.
Now the question is which kind of Presenter or Xaml Tag i should use inside the following ControlTemplate to my Views (UserControls) be shown.
<Style TargetType="{x:Type UserControl}" x:Key="MyItemContainerStyle">
<Setter Property="Background" Value="Brown"/>
<Setter Property="BorderBrush" Value="Blue"/>
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="Margin" Value="10"/>
<Setter Property="Template" >
<Setter.Value>
<ControlTemplate>
??????????????????????????
<!-- The following ContentPresenter not working and the Items dose not show -->
<ContentPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Set the ContentTemplate property of the style, not Control.Template or Template
An ItemsControl gets rendered like this :
<ItemsControl>
<ItemsPanel>
<ContentPresenter>
<ItemTemplate>
</ContentPresenter>
<ContentPresenter>
<ItemTemplate>
</ContentPresenter>
<ContentPresenter>
<ItemTemplate>
</ContentPresenter>
</ItemsPanel>
</ItemsControl>
The ItemContainerStyle applies to the ContentPresenter object that wraps each item in this XAML tree, and I don't believe a ContentPresenter has either a Control.Template or a Template property.
When changing how a ContentPresenter is displayed you should overwrite the ContentTemplate property instead.
This works for me in my test application:
<ItemsControl Background="#FF85664F" prism:RegionManager.RegionName="WorkspaceRegion">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="DarkGray" Padding="10"
Margin="5, 5, 5, 0" BorderThickness="1"
CornerRadius="5" Background="#FFC3BF8F">
<ContentPresenter Content="{Binding}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<system:String>ItemsControl Item #1</system:String>
<system:String>ItemsControl Item #2</system:String>
<system:String>ItemsControl Item #3</system:String>
<system:String>ItemsControl Item #4</system:String>
<system:String>ItemsControl Item #5</system:String>
</ItemsControl>
I need to set the ItemsPanelTemplate property of a listbox based on a dependency property on the control. How do I use the DataTemplateSelector to do that?
I have something like:
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<!-- Here I need to replace with either a StackPanel or a wrap panel-->
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
Thanks
There isn't an ItemsPanelSelector (probably because it isn't a DataTemplate) but you can bind it or use a Trigger
Binding example
<ListBox ItemsPanel="{Binding RelativeSource={RelativeSource Self},
Path=Background,
Converter={StaticResource MyItemsPanelConverter}}">
Trigger in Style example
<ListBox ItemsSource="{Binding Source={x:Static Fonts.SystemFontFamilies}}">
<ListBox.Style>
<Style TargetType="ListBox">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<!-- Your Trigger.. -->
<Trigger Property="Background" Value="Green">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.Style>
</ListBox>
I'm thinking the best route here would be to use a Style for your ListBox and set Triggers that change the ItemsPanel based on the DependencyProperty you reference.