Set different datacontext in one view - c#

I have 2 Windows. From the first window I'm calling second :
var window = new WindowButtonClick("Graphic") {DataContext = new GraphicViewModel()};
window.ShowDialog();
Here is XAML of second window:
<Window x:Class="WindowButtonClick"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:graphic="clr-namespace:Windows.Graphic"
WindowStartupLocation="CenterScreen" >
<Window.Resources>
<DataTemplate DataType="{x:Type graphic:GraphicViewModel}">
<graphic:Graphic />
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding}"/>
</Grid>
and constructor:
public WindowButtonClicks(string title)
{
InitializeComponent();
Title = Application.Current.Resources[title].ToString();
}
So how can I set DataContext, that it will show title that I pass in constructor as window Title and ContentControl will show one of the viewModels(GraphicViewModel in this case) ?

This is a common problem in WPF. Luckily, it has a simple solution. You'll need to use a RelativeSource Binding. So you'll need to set the DataContext to one object, whose properties you can data bind simply to like this:
<TextBox Text="{Binding PropertyOfDataContext}" />
And for any properties that are declared in the Window or UserControl, you can use the RelativeSource Binding like this:
<TextBox Text="{Binding PropertyOfWindow, RelativeSource={RelativeSource AncestorType={
x:Type YourXamlPrefix:YourWindow}}}" />
UPDATE >>>
You said:
I will have 20 ViewModels, that I want to load to ContentControl
If you had only provided all of the relevant information when asking your question, then you would have had a better answer by now. This is a different problem, but can be fixed just as easily. In this case, you can just set your view models to their views using DataTemplates... just define one for each view model/view pair like this:
<DataTemplate DataType="{x:Type ViewModels:ViewModel1}">
<Views:View1 />
</DataTemplate>
...
<DataTemplate DataType="{x:Type ViewModels:ViewModelN}">
<Views:ViewN />
</DataTemplate>
Note that I did not set the x:Key values... this means that the specified views will be implicitly rendered by the Framework whenever it comes across objects of the relevant type. Then to display the View1 from this example, you'd just need to do this:
<ContentControl Content="{PropertyOfTypeViewModel1}" />
UPDATE 2 >>>
Wow... I really hope that you've explained your problem properly this time because this is my last update. So I can't really see a problem with what you are asking for... you want to set a property in the constructor to be displayed as the Window.Title. This would definitely work:
public WindowButtonClicks(string title)
{
InitializeComponent();
Title = "Some Title";
}
So if your code doesn't work, then you must have a problem with your call to Application.Current.Resources[title]... have you actually checked whether that returns a value or not? If it does, then you have a real problem, because it is perfectly acceptable to set the Window.Title like this.
If Application.Current is returning null, then just make sure that you set it to an instance of MainWindow.xaml.cs in the code behind:
// In MainWindow.xaml.cs constructor
Application.Current = this;
Other than that, your problem is impossible to determine from the information that you have provided.

Related

UWP+Prism MVVM - How do I set the value of a view model property via XAML?

I have a property in a view model which I would like to be able to set via the XAML but I can't figure out how to do it.
I have a pretty basic user control (containing a list of items), two of which are to be placed on a page and I would like to be able to set one to be a 'Source' (defined by an enum) and one to be a 'Target'.
[The code below has been stripped down quite a bit so apologies if I've accidentally made some mistakes or missed something out.]
My enumeration is:
public enum ConversionSide
{
Source, // Convert something FROM whatever is here.
Target // Convert something TO whatever is here.
}
I have a page which looks like this:
<Page
x:Class="MyApp.Views.ConverterPage"
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:models="using:MyApp.Models"
xmlns:my="using:MyApp.Controls"
xmlns:prismMvvm="using:Prism.Windows.Mvvm"
prismMvvm:ViewModelLocator.AutoWireViewModel="True"
Style="{StaticResource PageStyle}"
mc:Ignorable="d">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<my:SelectorPage Name="SourceSelector" Grid.Column="0" />
<my:SelectorPage Name="TargetSelector" Grid.Column="1" />
</Grid>
</Page>
...where SelectorPage is a user control (I've called it a 'Page' to make the Prism AutoWire work but that's not the issue here) containing a list of items (all working fine) which looks like this...
<UserControl
x:Class="MyApp.Controls.SelectorPage"
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:models="using:MyApp.Models"
xmlns:my="using:MyApp.Controls"
xmlns:prismMvvm="using:Prism.Windows.Mvvm"
prismMvvm:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d">
<ListView
Grid.Column="0"
ItemsSource="{x:Bind ViewModel.MyList, Mode=OneWay}"
SelectedItem="{x:Bind ViewModel.MySelectedItem, Mode=TwoWay}">
<ListView.Header>
<TextBlock Margin="0,8,0,8" HorizontalAlignment="Center" FontStyle="Italic" Text="Header Text" />
</ListView.Header>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:MyListItem">
<my:MyListItemTemplate />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</UserControl>
..with code behind as...
public sealed partial class SelectorPage : UserControl
{
private SelectorViewModel ViewModel => DataContext as SelectorViewModel;
public SelectorPage()
{
this.InitializeComponent();
}
}
SelectorViewModel looks like this...
public class SelectorViewModel : ViewModelBase
{
private ConversionSide _side;
public ConversionSide Side
{
get { return _side; }
set { SetProperty(ref _side, value); }
}
// Many lines have been omitted for 'clarity'.
}
I would like to be able to set the Side property of SelectorViewModel in XAML like this...
<my:SelectorPage Name="SourceSelector" Grid.Column="0" Side="Source" />
<my:SelectorPage Name="TargetSelector" Grid.Column="1" Side="Target" />
(Once Side has been set, I do not expect it to ever change.)
How can I do this?
I've looked at using a dependency property but I can't get it to change the property in SelectorViewModel. When I add one in SelectorPage it's visible in the XAML and I can set it but it doesn't actually do anything so I'm probably not using it right. Putting a dependency property in the view model doesn't sound right to me but I could be wrong.
I've had a look around the web - Microsoft documentation, blogs, articles, stack overflow, etc. - but I can't find anything that explains things well enough for me to figure out what I'm supposed to do. The writings I've found seem to be exclusively about getting information from a bound property - which I'm okay with - but what I'm after is setting a property from the XAML.
Can anyone give my any clues please? I don't know if I'm just a tiny step away from getting what I want or if I'm miles away.
This would set the Side property of the SelectorPage control to Source:
A view sets the property of a view model by two-way bind to it. For example, the following TextBox sets the string property of a view model called Test when you change the text in the TextBox:
<TextBox Text="{Binding Test, Mode=TwoWay}" />
So setting the property of a view model from the view typically applies to controls that handles some kind of input. Any default value of a source property should be defined in the view model:
private ConversionSide _side = ConversionSide.Source;
You shouldn't define the default values in the view.

MVVM - datatemplate creates a new view

I have two DataTemplates that gets switched depending on the current ViewModel. However whenever I switch my ViewModel, it seems to call the respective View's constructor and calls the InitializeComponent() call within the constructor, which means that whenever I switch the DataTemplate, it generates a new view that is bound to the respective DataTemplate. I am not sure why this is happening but is there a way to prevent the creation of a new View when switching ViewModels?
Below is the DataTemplates located at my MainView.
<Window.Resources>
<DataTemplate DataType="{x:Type viewModels:FirstPanelViewModel}">
<views:FirstPanelView />
</DataTemplate>
<DataTemplate DataType="{x:Type viewModels:SecondPanelViewModel}">
<views:SecondPanelView />
</DataTemplate>
</Window.Resources>
The template is being displayed in a ContentControl.
<ContentControl Grid.Row="1" Content="{Binding CurrentViewModel}" />
This is my SecondPanelView which is the same as my FirstPanelView, it's very simple.
public partial class FirstPanelView
{
public FirstPanelView()
{
InitializeComponent();
}
}
public partial class SecondPanelView
{
public SecondPanelView()
{
InitializeComponent();
}
}
My Ioc makes sure that I generate only one instance of the SecondPanelView
container.Register<IFirstPanelViewModel, FirstPanelViewModel>(new PerContainerLifetime())
container.Register<ISecondPanelViewModel, SecondPanelViewModel>(new PerContainerLifetime());
DataContext is being bounded in each view by a custom markup extension.
DataContext="{Binding Source={common:Locate}, Path=FirstPanelViewModel}"
DataContext="{Binding Source={common:Locate}, Path=SecondPanelViewModel}"
Which is just calling GetInstance of the respective ViewModel.
public IFirstViewModel FirstViewModel
{
get { return _container.GetInstance<IFirstPanelViewModel>(); }
}
public ISecondViewModel SecondViewModel
{
get { return _container.GetInstance<ISecondPanelViewModel>(); }
}
This is an old issue, but I was also struggling with this issue. The answer is to place the view instances directly in the resources and bind them to content controls in the data templates. If you do so, the view is instantiated only once.
<Window.Resources>
<views:FirstPanelView x:Key="FirstPanelViewKey"/>
<views:SecondPanelView x:Key="SecondPanelViewKey"/>
<DataTemplate x:Key="DT1">
<ContentControl Content="{StaticResource FirstPanelViewKey}" />
</DataTemplate>
<DataTemplate x:Key="DT2">
<ContentControl Content="{StaticResource SecondPanelViewKey}" />
</DataTemplate>
</Window.Resources>
I wasn't able to solve my problem even after extending ContentControl. The issue I ran into using that approach is that ContentControl's dependency property was not directly interfacable/overridable which forced me to hack on the existing dependency property. Also the intialization of a DataTemplate seems to fall deeper than the simple ContentControl.
So I decided to change the way that my views are being displayed by simply toggling their visibility. This approach worked for me since I essentially want my views to stay in the background doing its own thing and ready to be interfaced at its previous state in any moment.

Setting the datacontext of all sub-views in parent view

I have a XAML file of the form
CTC.XAML
<UserControl x:Class="KPI.CTC.UI.CTC"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:uc="clr-namespace:KPI.CTC.UI"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<TabControl Grid.Row="1">
<TabItem Header="Industry">
<uc:Industry DataContext="{}"/>
</TabItem>
<TabItem Header="Templates">
<uc:Templates DataContext="{}"/>
</TabItem>
<TabItem Header="Item Calcs">
<uc:ItemCalcs DataContext="{}"/>
</TabItem>
</TabControl>
</UserControl>
where Industry.xaml,Templates.xaml and ItemCalcs.xaml have respective ViewModels in a separate namespace KPI.CTC.ViewModel. I wished to know is it possible to set the DataContext of all the 3 views in CTC.xaml only. I do not want to perform the tedious task of setting the DataContext separately for individual views.
Thanks in advance!!
If you look at the FrameworkElement.DataContext Property page on MSDN, you should see a section named Dependency Property Information:
This tells us that the DataContext DependencyProperty has a FrameworkPropertyMetadata value of Inherits, which means that the DataContext property will automatically be inherited by child controls. Therefore, if you had wanted to set the DataContext of all of the child controls to the same value as the parent, then this would happen automatically, but this is the only situation where this would happen.
So unless your situation is as above, you're going to have to write some code somewhere... the only question is where. Perhaps the simplest method is for you to use DataTemplates, although this will only work if you are data binding an instance of a different type of object for each user control. Try adding these DataTemplates into your Application.Resources:
<DataTemplate DataType="{x:Type ViewModels:IndustryViewModel}">
<Views:Industry />
</DataTemplate>
...
<DataTemplate DataType="{x:Type ViewModels:ItemCalcsViewModel}">
<Views:ItemCalcs />
</DataTemplate>
Using this method will free you from ever having to manually set a DataContext property. Just add a new DataTemplate for each view/view model pair that you have and then you can display your views/UserControls like this:
<ContentControl Content="{Binding ViewModelIntanceProperty}" />
There are plenty of ways to do it and it depends on your application architecture, complexity, etc. Some involve using so-called IoC containers.
The three simplest would be:
Declare VMs directly in XAML:
<TabItem Header="Industry">
<uc:Industry>
<uc:Industry.DataContext>
<vm:IndustryViewModel />
</uc:Industry.DataContext>
</uc:Industry>
</TabItem>
etc.
Declare VMs as 'main' ViewModel properties:
public IndustryViewModel IndustryViewModel
{
get;
set;
}
etc.
If TabControl ViewModels inherit from one base class, you may delcare a Dictionary
in 'main' VM containing the 3 TabControl ViewModels:
Dictionary<string, ViewModelBase> ViewModels
{
get;
set;
}
ViewModels = new Dictionary<string, ViewModelBase>();
ViewModels.Add("Industry", new IndustryViewModel());
// and so on...
and assign it in XAML:
<TabItem Header="Industry">
<uc:Industry DataContext="{Binding ViewModels[Industry]}"/>
</TabItem>

Using Icons in WPF Database Driven Application Results

I am attempting to make a WPF application. The application needs to use a "list view" to show results of queries to the database. I have been able to successfully create the application (GUI, database, LINQ, etc.), however, the display of my query results appear more "gridlike".
The specifications for the project below show that each record that appears in the results needs to have a green circle icon next to it. I have removed the actual results from the images below to keep the contents of the database private.
I don't have enough Reputation Points to post images, so I posted pictures so a sample/testing domain that I use. You can see screenshots here of the WPF app and code here:
http://digitalworkzone.com/WPF.html
What am I doing incorrectly? Is there something I need to add or modify to my code to be able to get the green circles and more of a "list" style to display my query results?
Understand the WPF content model. http://msdn.microsoft.com/en-us/library/bb613548.aspx
Anything that has a 'Content' property basically behaves in two ways. If the 'Content' is set to something that derives from UIElement, then the class will manage it's own presentation. Anything else, however, will just get .ToString() called, and it's text displayed instead.
What this means in the long run is that everything in WPF can display anything. If you want to show a button in a button, you can. For example:
<Button>
<Button.Content>
<Button Content="This will show as text" />
</Button.Content>
</Button>
The inner button will have text, but the outer button will show a Button because Button derives from UIElement and therefore will handle its own presentation.
In your picture examples above, you have ListBoxes/DataGrids that you want to fill in with graphical information. Try this out:
<ListBox HorizontalContentAlignment="Stretch">
<ListBox.Items>
<Button Content="One"/>
<Button Content="Two"/>
<Button Content="Three"/>
<Button Content="Four"/>
</ListBox.Items>
</ListBox>
Now you have a ListBox that shows Buttons instead of Text. You can take this a step further and contain the items in a stackpanel, for example:
<ListBox HorizontalContentAlignment="Stretch">
<ListBox.Items>
<StackPanel Orientation="Horizontal">
<Button Content="A button"/>
<Label Content="Some text" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button Content="A button"/>
<Label Content="Some text" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button Content="A button"/>
<Label Content="Some text" />
</StackPanel>
</ListBox.Items>
</ListBox>
Now we have items that contain a layout container (StackPanels, which then contains other elements).
However, if you set the ItemsSource elsewhere, you can actually use a DataTemplate to display the contents. A DataTemplate in effect targets a particular class and lays out it's contents as defined in XAML. Consider:
Code Behind:
public partial class MyWindow : UserControl {
public MyWindow() {
InitializeComponent();
MyListBox.ItemsSource = new List<Person> {
new Person("Sam", "Smith"),
new Person("Jim", "Henson"),
new Person("Betty", "White"),
};
}
XAML:
<ListBox HorizontalContentAlignment="Stretch" x:Name="MyListBox" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<Label Content="{Binding FirstName}"/>
<Label Content="{Binding LastName}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Now when the Listbox displays, it will cycle through each of the items in the ItemsSource property, and then lay them out using the DataTemplate. It's possible to have the DataTemplate target specific classes by using the DataType property if you're using polymorphism (as in different types of people such as 'Cusomters' or 'Employees' which all derive from 'Person).
The problem with this approach is that you are setting the value of the items directly, which is bad form. It's better to define a class that handles all of the data for your view separately. Consider:
public class ViewModel {
// WPF will automatically read these properties using reflection.
public List<Person> People {
get {
return new List<Person> {
new Person("Sam", "Smith"),
new Person("Jim", "Henson"),
new Person("Betty", "White")
};
}
}
}
That will hold all the data for the view, now let's add it to the actual window. First we need to reference the namespace ('xmlns' means xml namespace):
<Window x:Class="Sharp.MyWindow"
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:lol="clr-namespace:Sharp">
The namespace is Sharp (the namespace where my stuff lives), and the alias we'll give it is lol. Now we attach our ViewModel class to the window by setting it to the DataContext property, as in:
<Window>
<Window.DataContext>
<lol:ViewModel />
</Window.DataContext>
</Window>
This makes all of the public properties on the ViewModel class available to the Window. This way, if we want to read the Persons information into our ListBox, we simply say:
<ListBox HorizontalContentAlignment="Stretch" ItemsSource="{Binding People}" >
...
</ListBox>
Notice that we say ItemsSource={Binding People}, which means 'scan the ViewModel for any public properties called 'People' and then retrieve those results. This is essentially the fundamentals behind the MVVM approach. You might have all of your business logic in one or many classes which handle the main application operation in a Model, but then you have a ViewModel which interacts with the Model and exposes the results as public properties. WPF automatically binds to those properties and presents them for your. The information just flows, rather than setting the values by force.
To really understand how WPF is supposed to work, you should take some time to understand the basics of MVVM. WPF was really designed with MVVM in mind, and so to really get how WPF is supposed to work, you really should take the time to get your head around it. Take a look at:
http://agilewarrior.wordpress.com/2011/01/11/simple-mvvm-walkthrough-part-i/ .
<ListBox ItemsSource="{Binding QueryResults}">
<ListBox.ItemsTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ImageSource}"/>
<TextBlock Text="{Binding TextSource}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemsTemplate>
</ListBox>
Will work if you have a list of objects named QueryResults in your code behind. Each object needs to have an string property named ImageSource and a string property named TextSource.
However, since you only need to display a green circle icon for each of the items, you can hardcode the image source. The above will work if you want to have a different icon for each, though.
Also note that in order for this to work, you need to set the DataContext of the window to DataContext="{Binding RelativeSource={RelativeSource Self}}"

Binding to code behind from custom control

I have a GridView that has several buttons. One of them is defined by the following template:
<DataTemplate x:Name="SubjectItemTemplate">
<Canvas Width="340" Height="170" VerticalAlignment="Top">
<Controls:ThreeImageButton HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,0,0,0"
NormalStateImageSource="{Binding NormalImage}"
HoverStateImageSource="{Binding HoverImage}"
PressedStateImageSource="{Binding PressedImage}" Command="{Binding Path=NavigateToUnitsPage}"
CommandParameter="{Binding}" Canvas.Left="0" Canvas.Top="0">
</Controls:ThreeImageButton>
</Canvas>
</DataTemplate>
Now I have a custom control as you can see, called ThreeImageButton. The button works fine when I use it on its own. But when I have it in the DataTemplate it won't bind properties to the code behind.
Right now, I have
x:Name="MyThreeImageButton"
in the custom button definition. And I connect to the code-behind like this:
<TextBlock Text="{Binding ElementName=MyThreeImageButton, Path=NormalStateImageSource}"/>
(This is just a test to display the text, in the actual code I would assign an image source to another property that is referred to by an element).
Right now, nothing is displayed in the TextBlock. What is the correct binding syntax I'm supposed to use to reach my properties?
Thanks!
Edit: I am setting the variable in the InitializeComponent function and I am using SetValue on the DependencyProperty.
Edit: Let me add the following information to be more clear
Scenario I:
In DataTemplate for GridView:
<UserControl CustomParameter="Literal Text">
In UserControl:
<TextBlock Text="{Binding CustomParameter}">
in UserControl .cs: this.DataContext = this
works!
Scenario II:
In DataTemplate for GridView:
<UserControl CustomParameter="{Binding ValueFromDataItem">
In UserControl:
<TextBlock Text="{Binding CustomParameter}">
in UserControl .cs: this.DataContext = this
nope!
I see,
So setting up a two-way binding to a custom property in a user control can be tricky because a user control cannot bind to a CLR property. Not only that but setting the data context on a user control has an unexpected effect on the binding inside it.
You can solve these problems with a little slight of code. Basically back your CLR properties with dependency properties and set the data context on a child element instead of the root user control.
Take a look at this sample. Let's pretend you have the following MainPage. That MainPage will eventually use our custom user control. So let's set the stage.
Here's the code-behind:
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.DataContext = new /* your view model */
{
Title = Guid.NewGuid().ToString(),
};
}
}
In the code above I am simulating a complex view model with a simple anonymous class. It would be silly for you to implement your own like this, but at the same time it is silly for me to build a simple sample with the complete scaffolding. I bring this up only so it does not confuse you - as it could look like I am suggesting this approach in prod.
Here's the XAML:
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<local:MyUserControl Text="{Binding Title}" />
</Grid>
In the XAML above, there is absolutely nothing special. I already have reference to the user control in the local namespace and I simply declare it here.
Okay, now that we have a consumer of the control, it's worth pointing out that in testing developers can mistakenly think that their binding is working because they test with literal values. Literal values bind fine. It's binding from the underlying view model that hick-ups.
Let's say another thing, some developers tend to avoid dependency properties because the require a little more typing. People remember that [kbd]propdp[/kbd] is a handy Visual Studio snippet that stubs out a dependency property for you.
Take a look at this user control. It has two controls, a TextBox and a TextBlock which are there to demonstrate the OneWay and TwoWay functionality of this binding approach. We also implement INotifyPropertyChanged on the user control. For the most part, adding a view model in the case of a user control is overkill because the user control already acts like a view model. It's up to the developer, but it seems dumb to me.
Here's the code behind:
public sealed partial class MyUserControl : UserControl, INotifyPropertyChanged
{
public MyUserControl()
{
this.InitializeComponent();
}
// text property
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValueDp(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(MyUserControl), null);
// bindable
public event PropertyChangedEventHandler PropertyChanged;
void SetValueDp(DependencyProperty property, object value,
[System.Runtime.CompilerServices.CallerMemberName] String propertyName = null)
{
SetValue(property, value);
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
In the ode above, I have create a "Text" property and backed it with a dependency property. For a matter of reuse I have also implemented SetValueDp() which could be used again and again if I had more than a single property. Even though this demo has but one, I wanted to include this because the repetitive logic should certainly be abstracted out like this.
Here's the XAML:
<Grid Background="Black" DataContext="{Binding ElementName=userControl}">
<StackPanel>
<TextBox Text="{Binding Text, Mode=TwoWay}"
MinHeight="100" Padding="15" FontWeight="Light" FontSize="50" />
<TextBlock Text="{Binding Text}"
MinHeight="100" Padding="15" FontWeight="Light" FontSize="50" />
</StackPanel>
</Grid>
In the XAML above, I do nothing special insofar as binding. The syntax simply binds to the Text property using the Mode appropriate to the control. Just like you would do normally. However, what's worth noticing is that the DataContext is NOT set on the user control. Instead, it is set on the Grid. As a point of fact, any control in the tree other than the user control could be used like this. Just don't set the data context of the user control.
That is it by the way.
I have tested it to make sure it works. Demonstrating both one and two way binding is pretty handy here. I might even turn this into a blog in case other developers want to find it and don't discover this question. Thanks for your question!
Best of luck!
As the comments alluded to, your DataTemplate is placing the datacontext of the items to whatever object you are adding to your list. This is not the same as the surrounding user control's data context. If you want to reference that datacontext's commands, do the following in the DataTemplate's bindings:
{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.NormalImage}
What this is saying is to go out and find the user control ancestor and use its datacontext and then look for the NormalImage property. If you run into problems, check your output window for binding errors. It is very helpful in finding binding problems.

Categories