So I have this problem. I want to data trigger a column visibility for a grid in a WPF, but I can't get to bind the property name from the view with the actual property from the ViewModel.
View looks like this:
<Page x:Class="ImageViewer.Pages.ImageViewPage">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="SidePanelColumn">
<ColumnDefinition.Style>
<Style TargetType="ColumnDefinition">
<Setter Property="Width" Value="250"/>
<Setter Property="MinWidth" Value="250"/>
<Style.Triggers>
<DataTrigger Binding="{Binding}" x:Name="SidePanelColumnVisibility" Value="True">
<Setter Property="Width" Value="0"/>
<Setter Property="MinWidth" Value="0"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ColumnDefinition.Style>
</ColumnDefinition>
The binding is done like this (for other properties):
this.OneWayBind(ViewModel, vm => vm.AppCursor, v => v.Cursor).DisposeWith(cleanup);
here we bind the property name AppCursor from the ViewModel with the property Cursor from the View.
I would like to do the same with SidePanelColumnVisibility. My problem is that I can't seem to be able to access SidePanelColumnVisibility from the View.
The biding should look something like
this.OneWayBind(ViewModel, vm => vm.SidebarVisible, v => v.SidePanelColumnVisibility).DisposeWith(cleanup);
this.SidePanelColumn.Style.Triggers when inspecting this element I can see my trigger, but can't find a way to get the trigger by it's name SidePanelColumnVisibility.
I am fairly new to WPF ( been doing it for a week). I for sure somewhere misunderstood something, any help would be great.
LE:
The column looks like this
<ListBox Grid.Column="0"
MinWidth="250"
x:Name="FrameListBox"
BorderThickness="0"
HorizontalContentAlignment="Stretch"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Visible">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="1"></UniformGrid>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Image MinWidth="200" Height="Auto" Width="Auto" Source ="{Binding}" Stretch="Uniform"></Image>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Related
I have here a ListBox which shall show serveral items where some of them are selected.
<ListBox IsEnabled="False" x:Name="SecondaryModelSelector" SelectionMode="Extended" SelectedItem="{Binding Path=DataContext.SelectedSecondaryModels, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContentControl}, Mode=OneWay}" ItemsSource="{Binding Path=DataContext.AvailableModels, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContentControl}}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Name}" Margin="5,0,5,0"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The selected items are set in a ComboBox and thus the ListBox is disabled and Mode=OneWay to disable selection in the ListBox itself.
The Template looks like this:
<Style x:Key="MyListBoxItem" TargetType="ListBoxItem">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border x:Name="Border" Padding="2">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="Border" Property="Background" Value="Blue"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="MyListBoxBorder" TargetType="{x:Type ListBox}">
<Setter Property="ItemContainerStyle" Value="{StaticResource MyListBoxItem}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBox">
<Border x:Name="OuterBorder">
<ScrollViewer Margin="0" Focusable="false">
<StackPanel Margin="2" IsItemsHost="True" />
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The problem is now, that I cannot display multiple selected items, i.e. the background does not turn blue. Showing only one selected item is no problem.
Works (but shows only one selected item...)
public Cpu SelectedSecondaryModels
{
get { return SelectedGlobalVariable.SecondaryModels.FirstOrDefault(); }
}
Does not work:
public ObservableCollection<Model> SelectedSecondaryModels
{
get { return new ObservableCollection<Model>(SelectedGlobalVariable.SecondaryModels); }
}
I have no errors, so how can I show multiple selected items?
I tried SelectedItems instead of SelectedItem instead, but this is read-only field.
When I allow to select items in the ListBox than the blue background appears, and binding throws errors as there is no setter method.
The SelectedItem property cannot be bound to a collection of several items to be selected.
And as you have already noticed, SelectedItems is a read-only property.
You could use an attached behaviour to work around this. Please refer to this blog post and this example for more information about how to do this.
You will also find some solutions here:
Bind to SelectedItems from DataGrid or ListBox in MVVM
I'm learning WPF and developing a dynamic Menu which is driven by data binding of it's ItemsSource to an ObservableCollection. To do this I have a simple MenuItemViewModel and a HierarchicalDataTemplate for automatic binding of MenuItems to it.
The problem I have is that Command property doesn't work for top level menu items. Despite it is set, a MenuItem doesn't react on mouse click and doesn't get disabled if Command cannot be executed. Simply it's like it's not getting bound.
For lower level menu items though, it works as intended. I think this should be a problem of my HierarchicalDataTemplate but I can't find it, because as I see there's no code in template which might affect command binding of only top-level MenuItems.
MenuItemViewModel implements INotifyPropertyChanged and contains following public properties:
string Text
Uri ImageSource
ICommand Command
ObservableCollection<MenuItemViewModel> Children
HierarchicalDataTemplate for MenuItem in my Window.Resources is as follows:
<HierarchicalDataTemplate DataType="{x:Type common:MenuItemViewModel}"
ItemsSource="{Binding Path=Children}">
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command"
Value="{Binding Command}" />
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ImageSource}" />
<TextBlock Text="{Binding Text}" VerticalAlignment="Center"/>
</StackPanel>
</HierarchicalDataTemplate>
Can you please point me on my mistake?
EDIT: Top-level MenuItem doesn't contain any children (i.e. Children collection of associated ViewModel is empty).
Thanks to #sTrenat comment I've come to solution below.
<Menu.Resources>
<!-- cancel sharing of image so Icon will work properly -->
<Image x:Key="MenuIcon" Source="{Binding ImageSource}" x:Shared="False"/>
</Menu.Resources>
<Menu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}"
BasedOn="{StaticResource {x:Type MenuItem}}">
<Setter Property="Header" Value="{Binding Text}" />
<Setter Property="Icon" Value="{StaticResource MenuIcon}"/>
<Setter Property="Command" Value="{Binding Command}"/>
<Setter Property="ItemsSource" Value="{Binding Children}" />
<!-- centering MenuItem's Header -->
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Content="{Binding}" />
</DataTemplate>
</Setter.Value>
</Setter>
<!-- setting Icon to null when ImageSource isn't specified -->
<Style.Triggers>
<DataTrigger Binding="{Binding ImageSource}"
Value="{x:Null}">
<Setter Property="Icon" Value="{x:Null}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Menu.ItemContainerStyle>
Some time ago i asked here how to bind the expanded event to the viewmodel and came to a solution using AttachedCommandBehavior:
<TreeView Name="tv" ItemsSource="{Binding TreeViewElements}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children, Mode=OneTime}">
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Title, Mode=OneTime}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="local:CommandBehavior.Event" Value="Expanded"></Setter>
<Setter Property="local:CommandBehavior.Command" Value="{Binding DataContext.ExpandCommand, ElementName=tv}"></Setter>
<Setter Property="local:CommandBehavior.CommandParameter" Value="{Binding}"></Setter>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
Now it is necessary to bind the collapsed event, too.
Adding this to the style section like this does not work, only the collapsed event is binded:
<Setter Property="local:CommandBehavior.Event" Value="Expanded"></Setter>
<Setter Property="local:CommandBehavior.Command" Value="{Binding DataContext.ExpandCommand, ElementName=tv}"></Setter>
<Setter Property="local:CommandBehavior.CommandParameter" Value="{Binding}"></Setter>
<Setter Property="local:CommandBehavior.Event" Value="Collapsed"></Setter>
<Setter Property="local:CommandBehavior.Command" Value="{Binding DataContext.CollapseCommand, ElementName=tv}"></Setter>
<Setter Property="local:CommandBehavior.CommandParameter" Value="{Binding}"></Setter>
Then i found an example on the AttachedCommandBehavior homepage to use a collection of behaviors:
<local:CommandBehaviorCollection.Behaviors>
<local:BehaviorBinding Event="MouseLeftButtonDown" Action="{Binding DoSomething}" CommandParameter="An Action on MouseLeftButtonDown"/>
<local:BehaviorBinding Event="MouseRightButtonDown" Command="{Binding SomeCommand}" CommandParameter="A Command on MouseRightButtonDown"/>
</local:CommandBehaviorCollection.Behaviors>
The problem is that adding such a collection in the style section does not work, visual studio gives the error that the behavior property can not be attached to style.
Has anybody an idea how i can bind both events to the viewmodel?
Do it like this:
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="local:CommandBehaviorCollection.Behaviors">
<Setter.Value>
<local:BehaviorBinding Event="MouseLeftButtonDown" Action="{Binding DoSomething}" CommandParameter="An Action on MouseLeftButtonDown"/>
<local:BehaviorBinding Event="MouseRightButtonDown" Command="{Binding SomeCommand}" CommandParameter="A Command on MouseRightButtonDown"/>
</Setter.Value>
</Setter>
</Style>
On the main page of my application I have something like this, showing different subsections and respecting the 80 pixel margin between sections.
<GridView>
<GridView.Style>
<Style TargetType="GridView">
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="GridViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Margin" Value="0, 0, 80, 0"/>
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GridViewItem">
<ContentPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
</GridView.Style>
<Grid>
<TextBlock Text="SubTitle1"/>
...
</Grid>
<Grid>
<TextBlock Text="SubTitle2"/>
...
</Grid>
<Grid>
<TextBlock Text="SubTitle3"/>
...
</Grid>
</GridView>
Now I want to add Semantic Zoom to this, so that when I zoom out I see the sub section titles a bit like the Weather app. I have used ItemsSource to do SemanticZoom in the past but how do I do it if I'm placing the actual items straight into my GridView and there is no grouping going on.
EDIT: How are other people handling creating these types of seperate sub sections seperated by 80 pixels? To get SemanticZoom working do both GridViews have to bind to the same collections?
Your GridView above will need to have a name, let's call it ZoomedInGV. This of course becomes the control inside of SemanticZoom.ZoomedInView. Then you'll need to create another GridView, let's call it ZoomedOutGV. Finally, you'll need to create a binding between the two views. The solution should look something like this:
<SemanticZoom>
<SemanticZoom.ZoomedInView>
<GridView x:Name="ZoomedInGV">
...
</GridView>
</SemanticZoom.ZoomedInView>
<SemanticZoom.ZoomedOutView>
<GridView x:Name="ZoomedOutGV" ItemsSource="{Binding ElementName=ZoomedInGV, Path=Items}"/>
</SemanticZoom.ZoomedOutView>
</SemanticZoom>
I am using the MVVM light framework. On one of my pages I have a listbox which has a bound list and a bound selected item. I have a style that should highlight the selected item in that listbox. However, the item is not highlighted until I physically click it (It will not highlight the item when it is just bound).
XAML:
<UserControl.Resources>
<hartvm:ViewLocator x:Key="HartvilleLocator" d:IsDataSource="True" />
<DataTemplate DataType="{x:Type Quotes:QuotePetSummaryItem}">
<Views:QuoteSummaryItem DataContext="{Binding}" />
</DataTemplate>
<Style x:Key="ListboxItemStyle" TargetType="{x:Type ListBoxItem}">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Red"/>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Black"/>
</Style.Resources>
<Setter Property="BorderBrush" Value="Red"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="False">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Opacity" Value="40"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Opacity" Value="100"/>
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<UserControl.DataContext>
<Binding Source="{StaticResource HartvilleLocator}" Path="QuoteSummary" />
</UserControl.DataContext>
<Border>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Border BorderBrush="Black" BorderThickness="0">
<TextBlock TextWrapping="Wrap" Text="Quote Summary" Margin="5,0,0,0" FontSize="21.333" Foreground="{DynamicResource ControlHeaderBrush}" FontFamily="Verdana"/>
</Border>
<ScrollViewer d:LayoutOverrides="Height" ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto" Grid.Row="1" Margin="10,10,5,10">
<telerik:RadBusyIndicator IsBusy="{Binding IsProcessing}">
<ListBox ItemsSource="{Binding DefaultList}" SelectedItem="{Binding SelectedDefault}" Background="{x:Null}" ItemContainerStyle="{StaticResource ListboxItemStyle}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</telerik:RadBusyIndicator>
</ScrollViewer>
</Grid>
</Border>
</UserControl>
Code (To programmatically select the item in the list box):
public QuotePetSummaryItem SelectedDefault { get; set; }
private void SelectDefault()
{
if (DefaultList== null || DefaultList.First().Pet == null) return;
SelectedDefault = DefaultList.First();
MessengerService.Send(SelectionMessage.Create(SelectedDefault.SubDefault));
}
The message being sent is not important and does some other work on another page. The SelectedDefault property is the only thing that is used for this example.
Someone have an idea as to what I need to do to get this working?
It looks like the binding (and thus your view) doesn't know that the selected item property has changed.
In the setter of SelectedDefault you need to create a notification of some sort using the INotifyPropertyChanged interface.
I just took a quick look at the MVVM light framework and judging by the samples, if your viewmodel inherits from ViewModelBase use a field-backed property and call RaisePropertyChanged:
private QuotePetSummaryItem _selectedDefault;
public QuotePetSummaryItem SelectedDefault
{
get { return _selectedDefault; }
set
{
_selectedDefault = value;
RaisePropertyChanged("SelectedDefault");
}
}
1st you have to set the binding mode for selecteditem to twoway
<ListBox ItemsSource="{Binding DefaultList}" SelectedItem="{Binding SelectedDefault, Mode=TwoWay}" Background="{x:Null}" ItemContainerStyle="{StaticResource ListboxItemStyle}">
2nd in your viewmodel you have to implement INotifyPropertyChanged and raise it properly. look at Rock Counters answer.
if all this not work check your vs output window for binding errors.