WPF databinding very slow - c#

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.

Related

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>

Bind data to different element based on function/content of the data

While reading about the datatemplates I noticed you can choose different templates based on the type of data. - However can one also do this for different content of the data?
My modelview provides a list of data, in principle it's just a list of tuples (bound in a custom class to make typing easier) with Tuple<ImageData, AltText>.
The type of the property in the ModelView is:
ReadOnlyObservableCollection<ThumbDispData>
With ThumbDispData:
public class ThumbDispData
{
public ImageData Idhl { get; set; }
public string AltText { get; set; }
}
Now I wish to display an Image if it can (ImageData.Source is non null) - Otherwise it should show an alt-text.
The xaml or the usercontrol:
<UserControl x:Class="test.ThumbPanel"
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:test="clr-namespace:test"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<DataTemplate DataType="{x:Type test:ThumbDispData}">
<TextBlock Text="{Binding AltText}"></TextBlock>
</DataTemplate>
</UserControl.Resources>
<Grid Background="Transparent">
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
</UserControl>
Well above only shows the alt-text (which works): how would I create a selector based on the content of ThumbDispData.
You could use a DataTemplateSelector:
<UserControl.Resources>
<DataTemplate x:Key="tt" DataType="{x:Type test:ThumbDispData}">
<TextBlock Text="{Binding AltText}"></TextBlock>
</DataTemplate>
<DataTemplate x:Key="img" DataType="{x:Type test:ThumbDispData}">
<Image Source="{Binding Idhl}" />
</DataTemplate>
<local:Selector x:Key="selector" TextTemplate="{StaticResource tt}" ImageTemplate="{StaticResource img}" />
</UserControl.Resources>
<Grid Background="Transparent">
<ItemsControl ItemsSource="{Binding}" ItemTemplateSelector="{StaticResource selector}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
public class Selector : DataTemplateSelector
{
public DataTemplate ImageTemplate { get; set; }
public DataTemplate TextTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
ThumbDispData data = item as ThumbDispData;
if (data != null && data.Idhl != null)
return ImageTemplate;
return TextTemplate;
}
}
would I create a selector based on the content of ThumbDispData.
You would typically write a subclass of DataTemplateSelector, in which you check the Idhl property and return a different template object depending on whether it's null or not. Typically, the template would be a resource, which you load dynamically in the selector.
But your scenario is simple enough that I would use styles instead. For example:
<DataTemplate DataType="{x:Type test:ThumbDispData}"
xmlns:p="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Grid>
<TextBlock Text="{Binding AltText}">
<TextBlock.Style>
<p:Style TargetType="TextBlock">
<Setter Visibility="Visible"/>
<p:Style.Triggers>
<DataTrigger Binding="{Binding Idhl}" Value="{x:Null}">
<Setter Visibility="Collapsed"/>
</DataTrigger>
</p:Style>
</p:Style>
</TextBlock.Style>
</TextBlock>
<Image Source="{Binding Idhl}">
<Image.Style>
<p:Style TargetType="Image">
<Setter Visibility="Collapsed"/>
<p:Style.Triggers>
<DataTrigger Binding="{Binding Idhl}" Value="{x:Null}">
<Setter Visibility="Visible"/>
</DataTrigger>
</p:Style>
</p:Style>
</Image.Style>
</Image>
</Grid>
</DataTemplate>
Notes:
Strictly speaking, you don't need the <Setter Visibility="Visible"/> in the TextBlock style, since that's the default value. I put it there because it helps document the symmetry between the styles of the two elements.
The xmlns:p declaration is there only as a Stack Overflow work-around, because the XML formatter doesn't handle the XAML Style element correctly otherwise. In real XAML, you can leave that out and just use the Style name directly instead of p:Style.

WPF MVVM - Send dynamic parameter to parent ViewModel

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

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>

Categories

Resources