Is it possible in silverlight to assign a same command to a set of buttons in a grid through style? or assign it at a common place instead of repeating?
I mean something like using this -
<Style TargetType="Button">
<Setter Property="Command" Value="{Binding Command1}"/>
</Style>
instead of -
<Button Command="{Binding Command1}"/>
<Button Command="{Binding Command1}"/>
<Button Command="{Binding Command1}"/>
...
<Button Command="{Binding Command1}"/>
It is possible. In a simple way you can add a Style to the Resources and use it as a StaticResource. Assuming that Command1 belongs to the ViewModel you are binding to the Window. If not, provide proper path to the Command1 to bind it properly.
<Window.Resources>
<Style x:Key="buttonCommandStyle" TargetType="Button">
<Setter Property="Command" Value="{Binding Path=DataContext.Command1}" />
</Style>
</Window.Resources>
Then use it as the Style for your Button
<Button Style="{StaticResource buttonCommandStyle}" />
What you are looking for is the ItemsControl.
<ItemsControl x:Name="MyItemsControl" ItemsSource="{Binding MyButtonItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding Command1}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The Binding will be evaluated against each item, therefore you have several ways of providing the command instance:
you could place the same command instance as a public property in each itemInstance
you could set a source or relativeSource for the Binding to have it evaluated against the DataContext of the ItemsControl instead of against each item: {Binding Path=DataContext.Command1, ElementName=MyItemsControl}
Related
I have a MVVM project with a View and a ViewModel in its DataContext.
In this project I have a class ComboBoxCustom which inherits from ComboBox. I define some additional functionality in my ComboBoxCustom class.
To this ComboBoxCustom class I assign a control template to define its appearance.
The (simplified) style defining the (simplified) control template looks like:
<Style TargetType="{x:Type lib:ComboBoxCustom}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type lib:ComboBoxCustom}">
<StackPanel>
<TextBlock Text="{TemplateBinding TextPropertyInComboBoxCustom}"/>
<ComboBox DataContext="{TemplateBinding DataContext}"
ItemsSource="{TemplateBinding ItemsSource}"
DisplayMemberPath="{TemplateBinding DisplayMemberPath}"
SelectedValuePath="{TemplateBinding SelectedValuePath}"
SelectedValue="{TemplateBinding SelectedValue}"
SelectedItem="{TemplateBinding SelectedItem}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Which resides in a ResourceDictionary. The real control template has some additional features which are left out since they are not relevant for the question.
I use this ComboBoxCustom control in my View using:
<lib:ComboBoxCustom ItemsSource="{Binding MyObservableCollectionOfMyObjects}"
TextPropertyInComboBoxCustom="MyText"
DisplayMemberPath="MyDescription"
SelectedValuePath="MyValue"
SelectedItem="{Binding SelectedMyObject, Mode=TwoWay}"/>
The view is ok, and all items get loaded in the ComboBox which I can select.
The problem is that when I select a different item in the ComboBox, the property SelectedMyObject in my ViewModel does not get updated and consequently its setter is not called. Therefore, the (correct) information about the selected object is not available in my ViewModel.
When I use <ComboBox .../> (without the TextPropertyInComboBoxCustom property) instead of <lib:ComboBoxCustom .../> everything works just fine but then I don't have the additional functionality defined in ComboBoxMessage which I need.
Can anyone tell me what is wrong and how to fix this issue so I can use ComboBoxMessage in my view? Preferably without breaking the MVVM pattern.
Thank you!
Thanx to ASh's comment and information in this post.
The problem is that the TemplateBinding is one way. Therefore, all information from the ViewModel can get into the controls in the template. But not the other way around.
The solution is to specify a normal binding as:
SelectedItem ="{Binding SelectedItem, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
Whichs does about the same as a TemplateBinding but is two way.
The control template has become:
<Style TargetType="{x:Type lib:ComboBoxCustom}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type lib:ComboBoxCustom}">
<StackPanel>
<TextBlock Text="{TemplateBinding TextPropertyInComboBoxCustom}"/>
<ComboBox DataContext="{TemplateBinding DataContext}"
ItemsSource="{TemplateBinding ItemsSource}"
DisplayMemberPath="{TemplateBinding DisplayMemberPath}"
SelectedValuePath="{TemplateBinding SelectedValuePath}"
SelectedValue="{TemplateBinding SelectedValue}"
SelectedItem ="{Binding SelectedItem, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I am not sure about the SelectedValue property though. With the template like this, it works both when I use the SelectedValue property or the SelectedItem property.
The Mode=TwoWay option can be omitted in the view since the default binding mode for SelectedItem is already two way. The view line becomes:
<lib:ComboBoxCustom ItemsSource="{Binding MyObservableCollectionOfMyObjects}"
TextPropertyInComboBoxCustom="MyText"
DisplayMemberPath="MyDescription"
SelectedValuePath="MyValue"
SelectedItem="{Binding SelectedMyObject}"/>
Bind SelectedValue to the property in ViewModel
SelectedValue="{Binding SelectedMyObject, Mode=TwoWay}"
in your lib:ComboBoxCustom
I have a nested context menu like this:
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Thing Count" ItemsSource="{Binding ThingsProvider}">
NB the Thing Count parent MenuItem with children from ThingsProvider.
ThingsProvider provides a List of ThingViewModel which contains properties Thing and IsChecked. I want to be able to control IsChecked from my main view model and from the user via the MenuItem but I have hit a problem; If I use an ItemContainerStyle like this.
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Path=Thing, Mode=OneWay}"/>
<Setter Property="IsChecked" Value="{Binding Path=IsChecked, UpdateSourceTrigger=PropertyChanged}"/>
</Style>
</MenuItem.ItemContainerStyle>
then (predictably I suppose as it's a style) it will observe the ViewModel's IsChecked but will not set it.
If I use an ItemTemplate like so
<MenuItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Thing, Mode=OneWay}"/>
</DataTemplate>
</MenuItem.ItemTemplate>
I can't get to the MenuItem's IsChecked property as I've only got the TextBlock. If I put a MenuItem in the ItemTemplate then I end up with nested MenuItems in my UI and it looks bad.
There must be a way to do this but I'm stumped at the moment, can anyone help?
Cheers
Rich
I am making a TreeView with multiple levels of information being displayed. What I am hoping to achieve, is to show a different view when the sub-tree is expanded and closed. So, when just looking at the list, I want to make a view which shows a brief status overview of my more detailed view that would be visible if you expanded that item in the list.
I know I could just create 2 viewmodels implementing the same Interface, and based on a boolean IsExpanded, I could set the ActiveViewModel to one or the other, but I was just curious if I could just have the one viewmodel and change its view based on that boolean instead to save memory.
-OR-
Alternatively, should I just put 2 StackPanels into the same View, and then bind a visibility to be inverse of each other, so only one can be shown at a time?
-CODE-
Here is my current code (Private information removed / generic representation):
Xaml:
<UserControl.Resources>
<Style x:Key="TreeViewItemStyle" TargetType="TreeViewItem">
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Padding" Value="5"></Setter>
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="FontFamily" Value="{StaticResource Univers57}"/>
<Setter Property="FontSize" Value="20" />
<Setter Property="IsExpanded" Value="{Binding IsExpanded}" />
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
</Style>
</UserControl.Resources>
<TreeView x:Name="TreeView"
HorizontalAlignment="Stretch"
ItemsSource="{Binding ItemViewModels}"
ItemContainerStyle="{StaticResource TreeViewItemStyle}">
<TreeView.Resources>
<HierarchicalDataTemplate
DataType="{x:Type viewModel:ItemViewModel}" ItemsSource="{Binding Tasks}">
<StackPanel Orientation="Horizontal" Visibility="{Binding IsVisible}>
<Image Source="../../Images/Image.png" Height="24" Width="24"/>
<TextBlock Text="{Binding Item.Name}" />
<Button Style="{StaticResource ButtonStyle}">Dispatch</Button>
</StackPanel>
<StackPanel Orientation="Horizontal" Visibility="{Binding IsOtherVisible}>
<Image Source="../../Images/Image2.png" Height="24" Width="24"/>
<TextBlock Text="{Binding Item.Name}" />
<Button Style="{StaticResource ButtonStyle}">Dispatch</Button>
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate
DataType="{x:Type viewModel:TaskViewModel}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Task.Name}" Width="200"/>
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
Both the ItemViewModel and TaskViewModel inherit from 'TreeViewModel' which implements INotifyPropertyChanged, and has IsExpanded and IsSelected.
IsVisible on the StackPanel Binding should be set based on IsExpanded's value. (It only shows up once you expand the item. So, one stackpanel or the other should show up).
I have just had a play with the WPF Visual Tree tools in VS2015 and it looks like the IsExpanded isnt being changed when I expand/collapse the tree items. It only sets a value during creation of the viewmodels, after that it will never change - even though they physically open and close when running the program.
Have managed to find a solution.
Firstly, needed to set 2 Way Binding on the binding to IsExpanded.
Secondly, I have overridden / hidden IsExpanded by declaring it as new in the derived class.
Now when set() gets called on IsExpanded, I can make a change to the IsVisible and IsOtherVisible before sending OnPropertyChange(); Now that I can change the IsVisible's, they can fire their own OnPropertyChange() and all is good in the world again.
I have a problem with the RibbonMenuButton. Currently I have :
<RibbonMenuButton Label="Meeting" Width="Auto" ToolTipDescription="Display requests on the agenda for the meeting selected" ToolTipTitle="Meeting"
LargeImageSource="pack://application:,,,/Resources/meeting.png"
ItemsSource="{Binding MeetingsAvailable}">
<RibbonMenuButton.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value}"/>
</DataTemplate>
</RibbonMenuButton.ItemTemplate>
</RibbonMenuButton>
My MeetingsAvailable is actually a Dictionary<int, string>. This code is working, the RibbonMenuButtonis well displaying each Value of the dictionnary.
Now I'm trying to get back the Key of the MenuItem which has been clicked. My idea was to use a ICommand in my ViewModel and to bind an event to this command. But I don't really know how to get the event corresponding to clicking an Item in the RibbonMenuButton
Do someone have already did that ?
Thank you in advance.
You can data bind an ICommand to a RibbonMenuButton using the ItemContainerStyle property, like this:
<RibbonMenuButton Label="Meeting" ItemsSource="{Binding MeetingsAvailable}" ... >
<RibbonMenuButton.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value}"/>
</DataTemplate>
</RibbonMenuButton.ItemTemplate>
<RibbonMenuButton.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding DataContext.NameOfCommand,
RelativeSource={RelativeSource AncestorType={x:Type Views:View}}}" />
<Setter Property="CommandParameter" Value="{Binding Key}" />
</Style>
</RibbonMenuButton.ItemContainerStyle>
</RibbonMenuButton>
You have to create a command in your VM to which you can bind. Then you have to do a binding of the key of your dictionary to the command parameter so you can use it inside your commandfunction. Maybe you have to create an additional Button inside your DataTemplate.
I have the following code:
<Menu>
<MenuItem ItemsSource="{Binding SomethingMenuItems}" Header="Something"/>
</Menu>
Where MenuItems is a collection of objects of type SomethingMenuItem.
I also have:
<DataTemplate DataType="{x:Type SomethingMenuItem}">
<MenuItem Header="{Binding OrderTypeName}">
<MenuItem.Icon>
<Image Source="{Binding IconName}"/>
</MenuItem.Icon>
</MenuItem>
</DataTemplate>
I'd expect to get (I get something like this when I hardcode the menu items):
What I get instead is:
What am I doing wrong?
You might want to use ItemContainerStyle instead of DataTemplate. You have to style the container of the data item than just providing a template for data item.
With your DataTemplate, you basically displaying another nested MenuItem as content for each MenuItem generated for your Menu Something, and your inner MenuItem has the image in the correct place. I am attaching VisualTree from Snoop here for your reference.
Below is the Style for the container of the data item (in this case a MenuItem):
<MenuItem ItemsSource="{Binding SomethingMenuItems}"
Header="Something">
<MenuItem.Resources>
<Image Source="{Binding IconPath}" x:Key="IconImage" x:Shared="False"/>
<Style TargetType="MenuItem" >
<Setter Property="Header" Value="{Binding Name}" />
<Setter Property="Icon" Value="{StaticResource IconImage}" />
</Style>
</MenuItem.Resources>
</MenuItem>
You can see no nested MenuItems when you apply the above style, have added image here
With the above style applied, this is how the Menu looks:
Refer to this MSDN page to know more about ItemContainerStyle.
So, sthotakura's answer set me on the right track, but the code he posted didn't quite work because the style got applied to the parent menuitem as well.
In case someone else has a similar problem and stumbles on this question, here's the code that works:
<MenuItem ItemsSource="{Binding SomethingMenuItems}"
Header="Something">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem" >
<Style.Resources>
<Image Source="{Binding IconPath}" x:Key="IconImage" x:Shared="False"/>
</Style.Resources>
<Setter Property="Header" Value="{Binding Name}" />
<Setter Property="Icon" Value="{StaticResource IconImage}" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>