WPF MVVM - Send dynamic parameter to parent ViewModel - c#

I'm creating a WPF application that will create an arbitrary number of circles in a Canvas. The user should be able to click a specific circle to "select" it, causing the circle to grow larger. Other GUI elements will also update with other data in the ViewModel for that circle.
The trick is that I need to be able to unselect all other circles when one is selected, so the circle VM itself can't handle the select method. I need the parent VM to be able to handle it - and I need to pass an ID of the specific circle that was clicked as a parameter. (I have this ID property created already in the circle VM.)
Here's what I've got so far:
<Window x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MyApp"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="300">
<Window.DataContext>
<local:ClientManagerVM/>
</Window.DataContext>
<Grid>
<ScrollViewer x:Name="Scroller" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Background="Black" Grid.RowSpan="1">
<Canvas x:Name="DrawingCanvas" Height="{Binding circlesVM.Height, Mode=OneWay}" Width="{Binding circlesVM.Width, Mode=OneWay}" HorizontalAlignment="Left" VerticalAlignment="Top" Background="Black">
<ItemsControl ItemsSource="{Binding circlesVM.circleList}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding x}"/>
<Setter Property="Canvas.Top" Value="{Binding y}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Ellipse Fill="White" Width="{Binding size, Mode=OneWay}" Height="{Binding size, Mode=OneWay}">
<Ellipse.InputBindings>
<MouseBinding Gesture="LeftClick" />
</Ellipse.InputBindings>
</Ellipse>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Canvas>
</ScrollViewer>
</Grid>
</Window>
How can I set up the MouseBinding inside my Ellipse to trigger a command in the ClientManagerVM with a parameter value of the ID of the specific Ellipse?

Something like this where YourCommand is an ICommand property of the "circlesVM" that accepts a circle as a command parameter:
<Ellipse Fill="White" Width="{Binding size, Mode=OneWay}" Height="{Binding size, Mode=OneWay}">
<Ellipse.InputBindings>
<MouseBinding Gesture="LeftClick"
Command="{Binding DataContext.circlesVM.YourCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}" />
</Ellipse.InputBindings>
</Ellipse>
You should read this though before asking another question though: https://stackoverflow.com/help/mcve

Related

Display icon over select images in wpf listbox

I've looked at some related answers (Content of a Button Style appears only in one Button instance, and Images only showing in last ListBoxItem), but can't seem to get their answers to work in my example.
My app wpf stack is relatively complex.
I've a UserControl within another window. Within the UserControl, I've a ListBox with nested elements ListBox.ItemTemplate > DataTemplate > Border > Grid > StackPanel
Within the StackPanel is a TextBlock, followed by an Image and a StackPanel.ToolTip
I'm wanting to place an icon over the Image, so I've further obfuscated the image by putting it in a Grid, and adding a ViewBox accessed via a Control Template (as suggested in the above links), so that the ViewBox is centered on the image. Here's the Grid:
<Grid>
<Image RenderOptions.BitmapScalingMode="HighQuality"
Height="{Binding ElementName=_this, Path=ThumbSize.Height}"
>
<gif:ImageBehavior.AnimatedSource>
<MultiBinding Converter="{c:ImageConverter}">
<Binding Path="ThumbLocation" />
<Binding Path="FullName" />
</MultiBinding>
</gif:ImageBehavior.AnimatedSource>
</Image>
<Control Template="{StaticResource PlaySymbol}" Visibility="{Binding PlayVisible}" />
</Grid>
The ViewBox's ControlTemplate is in the UserControl.Resources up at the top
<ControlTemplate x:Key="PlaySymbol" TargetType="{x:Type Control}">
<Viewbox Stretch="Uniform"
RenderTransformOrigin="0.5,0.5"
Opacity="0.75"
x:Shared="False"
>
<Viewbox.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="0.5" ScaleY="0.5"/>
</TransformGroup>
</Viewbox.RenderTransform>
<ContentControl Content="{StaticResource appbar_control_play}" />
</Viewbox>
</ControlTemplate>
The appbar_control_play is in the Resources directory in an Icons.xaml file.
<Canvas x:Key="appbar_control_play" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
<Path Width="20.5832" Height="31.6667" Canvas.Left="30.0833" Canvas.Top="22.1667" Stretch="Fill" Fill="{DynamicResource BlackBrush}" Data="F1 M 30.0833,22.1667L 50.6665,37.6043L 50.6665,38.7918L 30.0833,53.8333L 30.0833,22.1667 Z "/>
</Canvas>
The goal is to only display the icon for 'play' on movies. I've set the PlayVisible to return the proper visibility for movies, and not for other files. Yet, it is only displaying for the last movie. I've heard that this is the case for controls only able to have one parent. I've tried setting x:Shared="False" on the ViewBox, but to no avail.
The app works, but I've recently decided to add movies to the listing and want to display the play icon over their thumbnails, but not the other items. It seems simple on the outset, but I've yet to figure out what is needed.
Any help would be appreciated, otherwise I feel I may have to resort to overlaying the icon on the actual thumbnails of the movies.
It looks like the problem is not related to the Viewbox but the image resource appbar_control_play it references.
There is no need to add the Viewbox via a Control. Just add it directly to the DataTemplate.
Generally prefer a ContentControl over a templated Control if you wish to display content.
The x:Shared attribute is only required on a UIElement that is not part of a template but defined in a ResourceDictionary. For example, when you define the Viewbox in as a resource, you must set the x:Shared attribute to false. Otherwise it is only allowed to appear once in the visual tree.
In case the image resource is an image file, a proper DataTemplate could look as followed:
<DataTemplate>
<StackPanel>
<Grid>
<Image Source="path to image" />
<Image Source="path to overlay icon"
Stretch="UniformToFill"
Width="50"
Height="50" />
</Grid>
</StackPanel>
</DataTemplate>
In case the icon is a XAML resource like a Geometry or a Segoe MDL2 Assets font icon, the DataTemplate should look as followed:
App.xaml
<Application.Resources>
<Viewbox x:Key="PlayIcon" x:Shared="False">
<TextBlock FontFamily="Segoe MDL2 Assets"
Text="" />
</Viewbox>
<Viewbox x:Key="appbar_control_play"
x:Shared="False">
<Path Width="20.5832"
Height="31.6667"
Stretch="Fill"
Fill="{Binding RelativeSource={RelativeSource AncestroType=ContentControl}, Path=For4ground}"
Data="F1 M 30.0833,22.1667L 50.6665,37.6043L 50.6665,38.7918L 30.0833,53.8333L 30.0833,22.1667 Z " />
</Viewbox>
</Application.Resources>
MyControl.xaml
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Grid>
<Image Source="path to image" />
<ContentControl Content="{StaticResource appbar_control_play}"
Width="50"
Height="50"
Foreground="Pink" />
</Grid>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
Not sure what's happening on your side but the following just works:
<Window x:Class="abc.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
mc:Ignorable="d"
Title="Window1">
<Grid>
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<DataTemplate.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</DataTemplate.Resources>
<Border Width="64" Height="64" BorderBrush="Red" BorderThickness="1">
<Grid>
<TextBlock Text="{Binding}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
<Rectangle Fill="DeepSkyBlue"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Margin="2"
Width="16"
Height="16"
Visibility="{Binding Converter={StaticResource BooleanToVisibilityConverter}}"
x:Name="Button" />
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
<system:Boolean>True</system:Boolean>
<system:Boolean>False</system:Boolean>
<system:Boolean>True</system:Boolean>
<system:Boolean>False</system:Boolean>
<system:Boolean>True</system:Boolean>
<system:Boolean>False</system:Boolean>
</ListBox>
</Grid>
</Window>

How to tab once instead of twice in WPF

I have a simple page that allows me to associate names with bunks. Unfortunately tab isn't working as I expect. I have to tab once to get to the next item and again to get to the TextBox. This also occurs if I remove the Label control. I've searched and searched and tried a bunch of things but can't figure this one out. Any suggestions? I'm starting to get comfortable with WPF MVVM but I'm no expert.
<Page x:Class="DiverBoard.Views.ConfigurePage"
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"
xmlns:vm="clr-namespace:DiverBoard.ViewModels"
mc:Ignorable="d"
d:DesignHeight="400" d:DesignWidth="800"
Title="ConfigurePage" Background="Navy">
<Page.DataContext>
<vm:TripViewModel/>
</Page.DataContext>
<WrapPanel>
<ListBox Background="Navy" ItemsSource="{Binding Trip.Bunks}" KeyboardNavigation.TabNavigation="Cycle">
<ListBox.Template>
<ControlTemplate>
<DockPanel LastChildFill="True">
<ItemsPresenter></ItemsPresenter>
</DockPanel>
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label FontSize="18" Foreground="white" Content="{Binding Value.BunkNumber}" Width="50"></Label>
<TextBox FontSize="18" Text="{Binding Value.DiverName}" Width="200" IsTabStop="true"></TextBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</WrapPanel>
</Page>
Naturally I figured it out right after posting a question by reading something again that I had already read.
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsTabStop" Value="False"/>
</Style>
</ListBox.ItemContainerStyle>

ListViewItem not receiving OnMouseDownEvent and ListView not calling selection changed callback

I have the following:
<ListView SelectionMode="Multiple" SelectedIndex="{Binding SelectedIdx}" SelectionChanged="ItemsList_SelectionChanged" MinHeight="200" x:Name="ItemsList" ItemsSource="{Binding Items}" Background="Yellow" Grid.Row="1">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<!-- note that the list mode is wide enough to force a wrap to each new line -->
<WrapPanel Width="{Binding ActualWidth, ElementName=ItemsList}" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<local:Item/>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
Each individual 'Item' xaml has mousedown callbacks which aren't called but OnMouseOver is.
ItemsList_SelectionChanged is never called either.
The item xaml is pretty basic:
<ListViewItem x:Class="Controls.Item"
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"
xmlns:local="clr-namespace:Controls"
mc:Ignorable="d"
xmlns:ViewModel="clr-namespace:Controls.Controls.Item.ViewModel"
d:DesignHeight="50" d:DesignWidth="50"
Width="50"
Height="50"
Background="Blue"
Padding="0,0,0,0"
MouseLeftButtonDown="ListViewItem_MouseLeftButtonDown">
<!-- <Grid>
< ! - - <Label IsHitTestVisible="False" Width="50" Height="50" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontSize="10" Background="Pink" Content="{Binding Label}"/> - - >
</Grid> -->
</ListViewItem>
Like I said before, 'ListViewItem_MouseLeftButtonDown' doens't get called either but if there's a MouseOver event that does get called.
I believe its because the parent ListViewItem or ListView itself handle the PreviewMouseDown event and mark it as Handled="True".
I have 2 suggestions for you:
Do not use ListViewItem as the ItemTemplate. One is being created to wrap your ItemTemplate anyway.
Write a Behavior to use on your ItemTemplate's Content. The Behavior should register to PreviewMouseDown event and raise a Command(with Binding to it from the ListView's DataContext).

Binding to DataGrid of TabItem not Working using MVVM

I have a small application to help myself learn WPF and MVVM etc. I have been using the example by Josh Smith found here to construct my own application. I have got the application adding TabItems, but the embedded DataGrid which is bound to an ObservableCollection<ResourceViewModel> is not showing any data, see the image below:
The DataGrid is the section surrounded in blue. The UserControl also seems to be showing the in the tab itself for some reason, but that is not the problem I am asking about here. The UserControl contains a DataGrid which is bound as follows
<DataGrid ItemsSource="{Binding Resources}"
dataAccess:DataGridTextSearch.SearchValue="{Binding ElementName=searchBox,
Path=Text, UpdateSourceTrigger=PropertyChanged}"
AlternatingRowBackground="Gainsboro"
AlternationCount="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
...</DataGrid>
The Resources property is defined in the ViewModels namespace as
internal class ResourceDataViewModel : WorkspaceViewModel
{
readonly ResourceDataRepository resourceRepository;
public ObservableCollection<ResourceViewModel> Resources { get; private set; }
...
}
Where the ResourceViewmodel holds the information for each row of the DataGrid. I can confirm that the Resource property is populated. When I use the same model outside of MVVM and populate Resource in the same way it works. Can someone provide me with and idea of why this could be happening?
I have attempted to set the explicit path for the binding
ItemsSource="{Binding Path=(viewModels:Resources)}"
but this also does not work. Thanks for your time.
Edit. To address comments. I set the DataContext in the App.xaml.cs file by
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow window = new MainWindow();
// Create the ViewModel to which
// the main window binds.
MainWindowViewModel mainWindowViewModel = new MainWindowViewModel();
// When the ViewModel asks to be closed,
// close the window.
EventHandler handler = null;
handler = delegate
{
mainWindowViewModel.RequestClose -= handler;
window.Close();
};
mainWindowViewModel.RequestClose += handler;
// Allow all controls in the window to
// bind to the ViewModel by setting the
// DataContext, which propagates down
// the element tree.
window.DataContext = mainWindowViewModel;
window.Show();
}
The XAML of the MainWindow:
<Window x:Class="ResourceStudio.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModels="clr-namespace:ResourceStudio.ViewModels"
xmlns:views="clr-namespace:ResourceStudio.Views"
Title="MainWindow" Height="629.4" Width="814.4">
<Window.Resources>
<ResourceDictionary Source="MainWindowResources.xaml" />
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="284*"/>
<ColumnDefinition Width="567*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="48"/>
<RowDefinition Height="*"/>
<RowDefinition Height="24"/>
</Grid.RowDefinitions>
<DockPanel KeyboardNavigation.TabNavigation="None"
Background="#FFBEC8D8"
Grid.ColumnSpan="2"
Margin="0,0,0.4,0">
<Menu DockPanel.Dock="Top"
Background="#FFF9F9F9"
BorderBrush="Black"
KeyboardNavigation.TabNavigation="Cycle">
<MenuItem Header="_File">
<MenuItem Header="Load _Resource..."
Height="Auto"
Command="{Binding LoadResourceCommand}"/>
<MenuItem Header="_Add Language..."
Height="Auto"/>
<Separator/>
<MenuItem Header="Close _Workspace"
Height="Auto"
Command="{Binding CloseCommand}"/>
<MenuItem Header="E_xit"
Height="Auto" Command="{Binding CloseCommand}" />
</MenuItem>
<MenuItem Header="_Edit">
</MenuItem>
</Menu>
<ToolBarTray DockPanel.Dock="Top" MaxHeight="24" Background="#FFF9F9F9">
<ToolBar Background="#FFF9F9F9">
<Button ToolBar.OverflowMode="Never">One</Button>
<Button>Two</Button>
<Button>Three</Button>
</ToolBar>
</ToolBarTray>
</DockPanel>
<Grid Grid.Row="1" Grid.ColumnSpan="2" Margin="0,0,0.4,23.6" Grid.RowSpan="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TabControl ItemsSource="{Binding Path=Workspaces}"
Grid.Column="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
TabStripPlacement="Top"
Height="Auto"
Width="Auto">
</TabControl>
</Grid>
<StatusBar Grid.Row="2" Grid.ColumnSpan="2" Margin="0,0.4,0.4,-0.4">
<StatusBarItem DockPanel.Dock="Left" Background="#FF007ACC" Margin="0,2,0,0">
<TextBlock Text="Ready" Margin="5,0,0,0"/>
</StatusBarItem>
</StatusBar>
</Grid>
</Window>
Where the MainWindowResources.xaml is:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModels="clr-namespace:ResourceStudio.ViewModels"
xmlns:views="clr-namespace:ResourceStudio.Views"
>
<!--This template applies a ResourceControl view to an instance of the
ResourceDataViewModel class shown in the main window.-->
<DataTemplate DataType="{x:Type viewModels:ResourceDataViewModel}">
<views:ResourceControl/>
</DataTemplate>
<!--This template explains how to render the 'Workspace'
content area in the main window.-->
<DataTemplate x:Key="WorkspacesTemplate">
<TabControl
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}"
Margin="4"/>
</DataTemplate>
</ResourceDictionary>
The full code for the ResourceControl.xaml is:
<UserControl x:Class="ResourceStudio.Views.ResourceControl"
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"
xmlns:viewModels="clr-namespace:ResourceStudio.ViewModels"
xmlns:dataAccess="clr-namespace:ResourceStudio.DataAccess"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300" Name="control">
<DockPanel DataContext="{Binding ElementName=control}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBox Text="M" DockPanel.Dock="Top" Name="searchBox" />
<Grid DockPanel.Dock="Top">
<Border BorderBrush="#FF007ACC" BorderThickness="2" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<DataGrid ItemsSource="{Binding Path=(viewModels:Resources)}"
dataAccess:DataGridTextSearch.SearchValue="{Binding ElementName=searchBox, Path=Text, UpdateSourceTrigger=PropertyChanged}"
AlternatingRowBackground="Gainsboro" AlternationCount="2" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<DataGrid.Resources>
<dataAccess:SearchValueConverter x:Key="searchValueConverter"/>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="dataAccess:DataGridTextSearch.IsTextMatch">
<Setter.Value>
<MultiBinding Converter="{StaticResource searchValueConverter}">
<Binding RelativeSource="{RelativeSource Self}" Path="Content.Text" />
<Binding RelativeSource="{RelativeSource Self}" Path="(dataAccess:DataGridTextSearch.SearchValue)" />
</MultiBinding>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="dataAccess:DataGridTextSearch.IsTextMatch" Value="True">
<Setter Property="Background" Value="Orange" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="#FF007ACC"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
</DataGrid>
</Border>
</Grid>
</DockPanel>
</UserControl>
The TextBox is bound to the DataGrid. When the user types into that TextBox the DataGrid filters and highlights the cells which contains the required text. This however, is not the problem and this code works, it is just the binding to the DataGrid I am interested in. Thanks again for tour time.
Edit2: According to #dkozl's comments I have removed the DataContext="{Binding ElementName=control}" from the DockPanel declaration, so we now have
<DockPanel HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
...
and in the MainWindowResource.xaml I now have
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModels="clr-namespace:ResourceStudio.ViewModels"
xmlns:views="clr-namespace:ResourceStudio.Views"
>
<!--This template applies a ResourceControl view to an instance of the
ResourceDataViewModel class shown in the main window.-->
<DataTemplate DataType="{x:Type viewModels:ResourceDataViewModel}">
<views:ResourceControl DataContext="{Binding}"/>
</DataTemplate>
<!--This template explains how to render the 'Workspace'
content area in the main window.-->
<DataTemplate x:Key="WorkspacesTemplate">
<TabControl
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}"
Margin="4"/>
</DataTemplate>
</ResourceDictionary>
This has not worked. That is my DataGrid in the ResourceControl is not being populated. Thanks again for your time it is most appreciated...
Your UserControl DockPanel.DataContext is bound to ResourceControl control and not to ResourceDataViewModel class. What you need to do instead is to bind DataContext of ResourceControl in your DataTemplate. To achive that first remove DataContext="{Binding ElementName=control}" from ResourceControl.DockPanel and then bind ResourceControl.DataContext to your object by <views:ResourceControl DataContext={Binding}"/>. Also you need to change DataGrid items binding from ItemsSource="{Binding Path=(viewModels:Resources)}" to ItemsSource="{Binding Path=Resources}".
Not part of the original question but same template applies to tab header and tab content because DataTemplate is type specific and in this case tab header content and tab content is the same thing. To solve the issue remove DataTemplate for viewModels:ResourceDataViewModel type and put this directly into your main TabControl:
<TabControl.ContentTemplate>
<DataTemplate>
<views:ResourceControl DataContext={Binding}"/>
</DataTemplat‌​e>
</TabControl.ContentTemplate>

WPF databinding very slow

I had a question some time ago (this is NO duplicate) as you can see here: WPF Simple DataMatrix.
I asked about creating a matrix of LED lights on screen. I used to that the marked answer and created the matrix. It is displayed very well and I also applied commands on the Ellipse so I can edit the Matrix but also that works without any lag.
As result this is my code for the Matrix:
<ItemsControl x:Class="HTLED.WPF.Controls.LedGrid"
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"
xmlns:Data="clr-namespace:HTLED.Data;assembly=HTLED.Data"
xmlns:Controls="clr-namespace:HTLED.WPF.Controls"
xmlns:Commands="clr-namespace:HTLED.Client.Commands;assembly=HTLED.Client"
xmlns:Interactivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ViewModel="clr-namespace:HTLED.Client.ViewModel;assembly=HTLED.Client"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance ViewModel:LedContainerViewModel}" Name="ledGridRoot" >
<ItemsControl.Resources>
<DataTemplate x:Key="ledTemplate" DataType="{x:Type Data:Led}">
<Ellipse Name="ellipse" Fill="Green" Stretch="Uniform" SnapsToDevicePixels="True">
<Interactivity:Interaction.Triggers>
<Interactivity:EventTrigger EventName="PreviewMouseMove">
<Commands:CommandTrigger Command="{Binding ElementName=ledGridRoot, Path=DataContext.LedGridViewModel.LedMouseMoveCommand}" PassEventArgsToCommand="True"/>
</Interactivity:EventTrigger>
<Interactivity:EventTrigger EventName="PreviewMouseLeftButtonDown">
<Commands:CommandTrigger Command="{Binding ElementName=ledGridRoot, Path=DataContext.LedGridViewModel.LedOnCommand}" PassEventArgsToCommand="True"/>
</Interactivity:EventTrigger>
<Interactivity:EventTrigger EventName="PreviewMouseRightButtonDown">
<Commands:CommandTrigger Command="{Binding ElementName=ledGridRoot, Path=DataContext.LedGridViewModel.LedOffCommand}" PassEventArgsToCommand="True"/>
</Interactivity:EventTrigger>
</Interactivity:Interaction.Triggers>
</Ellipse>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=State}" Value="Off">
<Setter TargetName="ellipse" Property="Fill" Value="Red"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding}" ItemTemplate="{StaticResource ledTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Controls:StretchStackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
In the background I have a class called LedMatrix. It has a property with the collection of the leds:
ObservableCollection<ObservableCollection<Led>> _leds;
public ObservableCollection<ObservableCollection<Led>> Leds
{
get { return _leds ?? (_leds = CreateMatrix(XSize, YSize)); }
set { SetProperty(value, ref _leds, () => Leds); }
}
The matrix is contained by another control:
<Canvas x:Class="HTLED.WPF.Controls.LedContainer"
....
mc:Ignorable="d"
d:DataContext="{d:DesignInstance ViewModel:LedContainerViewModel}"
d:DesignHeight="300" d:DesignWidth="300" Name="layoutRoot" Drawing:DrawingCore.EnableDrawing="True">
<Viewbox Canvas.Top="0" Canvas.Left="0" Width="{Binding ElementName=layoutRoot, Path=ActualWidth}"
Height="{Binding ElementName=layoutRoot, Path=ActualHeight}">
<Grid>
<Controls:LedGrid Width="50000" Height="25000" Margin="500" DataContext="{Binding Path=Main.LedContainerViewModel}" ItemsSource="{Binding Path=LedContentContainer.Content}" />
</Grid>
</Viewbox>
As you can see I set the ItemsSource of the Matrix in the container. That Itemssource Is a Interface like this:
public interface ILedContentContainer
{
LedMatrix Content { get; set; }
}
And the LedMatrix I already showed before (the ObservableCollection<ObservableCollection<Led>>).
And now the very important:
I have the change the LedMatrix (the Itemssource of LedGrid - see LedGridContainer) very often because it is some kind of an animation. The problem is that that all is very very slow. So I wanted to ask if you know some optimations?
Again I have to change the LedMatrix very very fast.
If you apply a whole new ObservableCollection each time as Led changes, the complete grid has to be rendered again and again. You should change the State property of the Led's that are changing (they should fire PropertyChanged).
Also, if the amount of Led's is constant, you don't need an ObservableCollection at all. You can use any IEnumerable.

Categories

Resources