I have a ViewModel that is a Window
, inside this Window there are many UserControl
s that I have made. These work fine and the bindings and DataContext
s for each is set appropriately; all apart from one...
In my MainWindowView
XAML I have
<Controls:LogViewerView HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
DataContext="{Binding LogViewerViewModel}"/>
and in my MainWindowViewModel
I have
public LogViewerViewModel LogViewerViewModel { get; set; }
The LogViewerView
<UserControl x:Class="GambitFramework.Utilities.Controls.Views.LogViewerView"
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:Caliburn="http://www.caliburnproject.org"
xmlns:Models="clr-namespace:GambitFramework.Utilities.Models"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="200">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Resources/Styles.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<DockPanel>
<ItemsControl ItemsSource="{Binding LogEntries}"
Style="{StaticResource LogViewerStyle}">
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer CanContentScroll="True">
<ItemsPresenter/>
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DockPanel>
</UserControl>
where LogViewerViewModel
is
public class LogViewerViewModel : PropertyChangedBase
{
private BindableCollection<LogEntry> logEntries;
public LogViewerViewModel() { }
public LogViewerViewModel(IEnumerable<LogEntry> logEntries)
{
LogEntries = new BindableCollection<LogEntry>(logEntries);
}
public BindableCollection<LogEntry> LogEntries
{
get { return logEntries; }
set
{
logEntries = value;
NotifyOfPropertyChange(() => LogEntries);
}
}
}
and where in Styles.xaml we have
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Caliburn="http://www.caliburnproject.org"
xmlns:Models="clr-namespace:GambitFramework.Utilities.Models">
<Style x:Key="LogViewerStyle" TargetType="ItemsControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ScrollViewer CanContentScroll="True">
<ItemsPresenter/>
</ScrollViewer>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsItemsHost="True"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
<DataTemplate DataType="{x:Type Models:LogEntry}">
<Grid IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Timestamp" Width="Auto"/>
<ColumnDefinition SharedSizeGroup="Index" Width="Auto"/>
<ColumnDefinition SharedSizeGroup="IconSource" Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Timestamp}"
Grid.Column="0"
FontWeight="Bold"
Margin="5,0,5,0"/>
<TextBlock Text="{Binding Index}"
Grid.Column="1"
FontWeight="Bold"
Margin="0,0,2,0" />
<TextBlock Text="{Binding Message}"
Grid.Column="3"
TextWrapping="Wrap"/>
</Grid>
</DataTemplate>
</ResourceDictionary>
Where the model for LogEntry
is
public class LogEntry : PropertyChangedBase
{
private uint index;
private DateTime timestamp;
private IconPresentor iconSource;
private string message;
public uint Index
{
get { return index; }
set
{
index = value;
NotifyOfPropertyChange(() => Index);
}
}
public DateTime Timestamp
{
get { return timestamp; }
set
{
timestamp = value;
NotifyOfPropertyChange(() => Timestamp);
}
}
public string Message
{
get { return message; }
set
{
message = value;
NotifyOfPropertyChange(() => Message);
}
}
}
But my items are not being displayed and when I use Snoop to check the bindings
Cannot set Expression. It is marked as 'NonShareable' and has already been used
which clearly suggests the DataContext is not set correctly. What am I doing wrong here and why is my DataContext
not set for my control?
Thanks very much for your time.
Edit. Here is an answer using the same log control but binding to the code behind, I want to bind to a separate file: https://stackoverflow.com/a/16745054/626442
In my opinion there are several things that could help, first
I don't see where you have instanced this, but if it's defined too late it is not read by the view, so you can implement PropertyChangedBase in the viewmodels too (INotifiedPropertyChanged).
I am practically sure that you are having a null instead of the value.
In the case it is not check the following:
Another thing is be sure that all the properties of the customs controls are dependency properties well defined (the name of the control).
And you can test the following, instead of placing in the style place inside ItemTemplate of the itemscontrol, because I see DataTemplate directly instead of
You are binding the LogViewerViewModel to the DataContext of the MainWindowView instead of the DataContext of the LogViewerView
If you want to derive from parent's DataContext, have a look at similar questions like: How to access parent's DataContext from a UserControl
Notice that the DataTemplate is a bit special: https://stackoverflow.com/a/4480488
Have you tried being explicit with your binding:
You'll need a namespace for the main view:
Without seeing more of MainWindowView, I'd guess you've got a DataContext in between that hijacking your intent.
Refer the below code to get ride of codebehind and create a viewmodel.
I've investigated your code, and i didn't see setting DataContext for MainWindowView. There are a lot several options of doing this. For example 2 ways:
First - In your MainWindowView.xaml.cs set create and set your view model:
Second - Create and set vide model in your MainWindowView.xaml:
When you do one of the above it should just work.
Also i noticed, that you noticed redundunt code int your LogViewerView, it can be just :
because you have already write that code in LogViewerStyle inside your ResourceDictionary.
Hope this helps.
In
MainWindowView
you may have:But that's about it. You don't state that you are initializing it to anything. With that being said, I would think you need to have something like:
Everything else looks good, so my only thought is you aren't initializing your view model. Check your output window for any other binding errors and what not. Make sure your collection has items in it as well.