Bind to a MainPage Property out of DataTemplate - c#

I'm trying to bind an object defined in MainPage (CodeBehind) to a ConverterParameter inside a ListView DataTemplate:
<ListView ...>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text=".. SomeBindingExpression"
FontWeight="{Binding Converter={StaticResource ChangeDateToFontWeightConverter},ConverterParameter={Binding Source=MainPage,Path=Cache}}"/>
</TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Goal is to pass the "Cache" - Object defined in MainPage into the Converter. The obove Code calls the converter properly but the passed object Parameter is null. Is there any way to refer the Cache object in this XAML Code?

The ConverterParameter takes a string and can not be databound at all.
You can add a custom property to the converter class and pass your "Cache"-object in the code-behind of your MainPage.
((ChangeDateToFontWeightConverter)this.Resources["ChangeDateToFontWeightConverter"]).MyCustomProperty = myCacheObject;
In general i would advise to take an MVVM approach and extend your databound class with the required data from the cache object (if possible). That way you won't need the converter at all.

Related

Why is my DataContext getting switched out from under me for a binding?

I'm trying to use the PropertyGrid component from PropertyTools to display information on an object. I can bind the object easily enough -- it's a property on my DataContext -- but one of the things that can't be derived from the object is the name that should be displayed in the tab header. (And I can't change that; the object I'm inspecting comes from a third party.) The proper name is a different property on my DataContext.
PropertyGrid has a way to change the way the tab header is displayed, by passing a DataTemplate to its TabHeaderTemplate property. But something bizarre happens inside of the template: my DataContext is gone, replaced by something else. When I try to say {Binding TabName} in the appropriate place inside the context, it errors out and tells me that TabName is not a valid property on class Tab. But my DataContext class isn't called Tab; that's something inside of PropertyTools's codebase!
I'm still new to WPF, so I have no clue what's going on here. Why is the in-scope DataContext that's perfectly valid in the rest of the XAML file being yoinked out from under me inside this template, and how can I fix it?
EDIT: Posting the XAML as requested. The template is literally just the simplest possible thing:
<UserControl.Resources>
<DataTemplate x:Key="HeaderTemplate">
<TextBlock Text="{Binding TabName}" />
</DataTemplate>
</UserControl.Resources>
And then further down the page,
<props:PropertyGrid
SelectedObject="{Binding Value}"
TabHeaderTemplate="{StaticResource HeaderTemplate}" />
But for some bizarre reason, in the template it's trying to interpret the binding inside the wrong DataContext!
In this case, just be sure to specify the source in your binding. There are a few ways to do this. One is to use the RelativeSource property of the Binding. Another is to use ElementName
Give your UserControl this attribute:
x:Name="Root".
Then change your binding to use it
<TextBlock Text="{Binding ElementName=Root, Path=DataContext.TabName}" />
Or use this:
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:MyUserControl}}, Path=DataContext.TabName}"/>

WPF ItemsControl binding not updating when bound to an ObservableCollection in an object in the View Model

Basically I have an object in my view model that contains an ObservableCollection of a custom object. My XAML's DataContext is set to my ViewModel, my ViewModel contains a 'Scratchdisk' object, and the Scratchdisk object contains an ObservableCollection of Frame objects. Both the Scratchdisk and the Collection are set up as DependencyProperties.
In short: XAML --DataContext--> EditorViewModel --DependencyProperty--> Scratchdisk --DependencyProperty--> ObservableCollection<Frame>
The Frame object has 3 standard properties: Index, Image, and ImageUrl.
I'm trying to bind to the ObservableCollection in my XAML using this code:
<ItemsControl DataContext="{Binding Source=ThumbnailScratchdisk}" ItemsSource="{Binding Frames, UpdateSourceTrigger=PropertyChanged}" ItemTemplate="{StaticResource ThumbnailTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal"></VirtualizingStackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Where ThumbnailTemplate is defined in Window Resources as:
<DataTemplate x:Key="ThumbnailTemplate">
<Image Width="128" Height="96" Source="{Binding ImageUrl}"/>
</DataTemplate>
Theoretically, what should happen is, the Scratchdisk should receive filenames, create Frame objects, add them to the Collection, and then the binding should display them. The ObservableCollection is working and being populated, but the binding doesn't seen to be updating. All the updatable properties are set as DependencyProperties so the binding should update shouldn't it?
Links to the files:
XAML
ViewModel
Scratchdisk
Frame
The problem is in the binding of the DataContext of your ItemsControl.
You're setting it to "{Binding Source=ThumbnailScratchdisk}", but what you (presumably) want is to set it to just "{Binding ThumbnailScratchdisk}".
The DataContext of the page is already an instance of EditorViewModel, and you want the DataContext for the ItemsControl to bind to the property ThumbnailScratchdisk of that viewmodel.
Trying changing the binding path in XAML to ThumbnailScratchdisk.Frames

Pass parameter to UserControl in XAML

I'm using Syncfusion's DockingManager with the Adapter for MVVM so a List of ViewModels which implement IDockElement can be bound.
I'm using it this way:
<dm:DockingAdapter Grid.Row="1" ItemsSource="{Binding DockingItems}" />
In addition I'm using the latest version of Catel and its Custom controls.
The MVVM Adapter of the DockingManager needs these entries, to create a view from a ViewModel:
<DataTemplate DataType="{x:Type local:EventEditorViewModel}">
<Grid>
<events:EventEditorControl/>
</Grid>
</DataTemplate>
The problem is, that my EventEditorControl has a parameter in its constructor which needs the ViewModel.
How do I pass the correct ViewModel?
You cannot pass parameters to constructors using xaml.
Make you parameter a dependency property:
<events:EventEditorControl MyParameterAsDependencyProperty="{Binding SomeProperty}" />

Don't create new view each time with DataTemplate/DataType

I have something like this:
<Window>
<Window.Resources>
<DataTemplate DataType="{x:Type local:VM1}">
<!-- View 1 Here -->
</DataTemplate>
<DataTemplate DataType="{x:Type local:VM2}">
<!-- View 2 here -->
</DataTemplate>
<Window.Resources>
<ContentPresenter Content="{Binding}"/>
</Window>
This will automatically swap out the view as I bind different viewmodels, which is very handy.
However, I have one view with a tabcontrol and many subviews. Each subview has several visual parts that are configured by a custom xml file (complex business case). Each time this view is created, the xml file is parsed which causes a small (1-2 second) delay. It's enough of a delay to be annoying and make the UI feel sluggish.
Is there a way to use the DataTemplate pattern without destroying and recreating the view each time a viewmodel is bound? I'd rather not change the viewmodel if possible.
For this case the easiest solution is to have the two views always there and change which one is visible. You can use a converter to change the visibility based on the type of the data context
<View1 Visibility="{Binding Converter={StaticResource TypeToVisibilityConverter, ConverterParameter=VM1}" />
<View2 Visibility="{Binding Converter={StaticResource TypeToVisibilityConverter, ConverterParameter=VM2}" />
And the converter will check if the type matches with the parameter to return Visible, or Collapsed otherwise.
You could wrap your VM into an additional class. Your DataTemplates will decide on the type of the Wrapper class but the real implementation will be exposer through a property of this Wrapper. When this property will change the DataTemplate wont be reloaded but all the bindings will be refreshed.
Wrapper class:
public class WrapperVM1:INotifyPropertyChanged
{
public Content VM1 { get{...} set{...} }
}
public class WrapperVM2:INotifyPropertyChanged
{
public Content VM2 { get{...} set{...} }
}
Now your data templates will describe wrapper class representations:
<DataTemplate DataType="{x:Type local:WrapperVM1}">
<TextBlock Text={Binding Content.SomPropertyInVM1}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:WrapperVM2}">
<TextBlock Text={Binding Content.SomPropertyInVM2}"/>
</DataTemplate>
As you can see if you substitute the Content property of the wrapper with a new instance of VM this won't recreate the view but all bindings will update. However if you need to switch to other type of VM you will have to substitute the Wrapper class by the appropriate Wrapper.

How to create a template to display data from a class in WPF

I have a data layer which is returning lists of classes containing data. I want to display this data in my form in WPF. The data is just properties on the class such as Class.ID, Class.Name, Class.Description (for the sake of example)
How can i create a custom control or template an existing control so that it can be given one of these classes and display its data in a data-bound fashion.
Thanks :)
You could use a ListBox and set its ItemsSource property to the list containing your data items. Then you define a DataTemplate for your type like this:
<DataTemplate x:Key="MyDataTemplate" DataType="{x:Type MyType}">
<StackPanel>
<TextBlock Text="{Binding ID}"/>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Description}"/>
</StackPanel>
</DataTemplate>
...and tell the ListBox to use this DataTemplate by setting the ItemTemplate property.
It is also sufficient to just define the DataTemplate as above and give it no key. Then it will be used for all items which have the respective type.
BTW: You can find a more detailed example in MSDN on the page for the ItemTemplate property.

Categories

Resources