What is the datacontext of the user control? - c#

I have a user control like so:
<Grid>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding NameC}" Width="100" />
<TextBlock Text="{Binding Filename}" />
</StackPanel>
</Grid>
with DP in code behind:
public TestUc()
{
InitializeComponent();
DataContext = this;
}
public static readonly DependencyProperty NameCProperty = DependencyProperty.Register(
"NameC", typeof(string), typeof(TestUc), new PropertyMetadata(default(string)));
public string NameC
{
get { return (string) GetValue(NameCProperty); }
set { SetValue(NameCProperty, value); }
}
public static readonly DependencyProperty FilenameProperty = DependencyProperty.Register(
"Filename", typeof (string), typeof (TestUc), new PropertyMetadata(default(string)));
public string Filename
{
get { return (string) GetValue(FilenameProperty); }
set { SetValue(FilenameProperty, value); }
}
Now, when using it in a window,
this works fine:
<Window x:Class="TestDpOnUc.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:uc="clr-namespace:TestDpOnUc"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<uc:TestUc NameC="name is xxx" Filename="This is filename" />
</Grid>
But This does not:
<Window x:Class="TestDpOnUc.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:uc="clr-namespace:TestDpOnUc"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<uc:TestUc NameC="{Binding Name}" Filename="{Binding FileName}" />
</Grid>
public MainWindow()
{
InitializeComponent();
DataContext = this;
Name = "name is nafsafd";
FileName = "lkjsfdalkf";
}
private string _Name;
public string Name
{
get { return _Name; }
set
{
_Name = value;
OnPropertyChanged();
}
}
private string _FileName;
public string FileName
{
get { return _FileName; }
set
{
_FileName = value;
OnPropertyChanged();
}
}
Can someone please explain why? Why is the datacontext of the user control not automatically set to the parent - the main window?

It's because of this line DataContext = this in UserControl constructor. You set DataContext to your user control which affects default binding context for TestUc and all children, including <uc:TestUc ... />. So at the moment
<uc:TestUc NameC="{Binding Name}" Filename="{Binding FileName}" />
will look for Name and FileName properties inside UserControl. You need to remove that line but that will break bindings within the user control.
<TextBlock Text="{Binding NameC}" Width="100" />
<TextBlock Text="{Binding Filename}" />
Will look for NameC and Filename in MainWindow. Solution to that is to change binding context, per binding, via either RelativeSource or ElementName binding inside UserControl
<UserControl ... x:Name="myUserControl">
<Grid>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding ElementName=myUserControl, Path=NameC}" Width="100" />
<TextBlock Text="{Binding ElementName=myUserControl, Path=Filename}" />
</StackPanel>
</Grid>
</UserControl>

When creating a UserControl with DependencyProperties you have to bind your DP's within your UserControl with ElementName- or RelativeSource Binding to your Controls in your UserControl
<TextBlock Text="{Binding ElementName=myUserControl, Path=NameC}" Width="100" />
and you never set the DataContext.
DataContext = this; <-- do not do that within your UserControl
When you wanna use your UserControl you put it in your View and bind the Properties of your actual DataContext/Viewmodel to the DependencyProperties of the UserControl.
<uc:TestUc NameC="{Binding Name}" Filename="{Binding FileName}" />

Whe you are doing <uc:TestUc NameC="{Binding Name}" Filename="{Binding FileName}" /> its not looking at MainWindow's data context instead at your UserControl's data context.
So you might want to search for the right element and bind it. Below is one way to do it using ElementName by giving a name to Window like MainWindowName. Or you can also use relative source to search for its ancestor.
<Window x:Class="TestDpOnUc.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:uc="clr-namespace:TestDpOnUc"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
x:Name="MainWindowName">
<Grid>
<uc:TestUc NameC="{Binding Element=MainWindowName, Path=DataContext.Name}" Filename="{Binding Element=MainWindowName, Path=DataContext.FileName}" />
</Grid>

Related

How to use binding in WPF with Caliburn.Micro and with UserControl? [duplicate]

This question already has answers here:
Issue with DependencyProperty binding
(3 answers)
Closed 2 years ago.
I have created a UserControl with some fields. I would like to use it and bind data to these fields. Most of them are static values but the DataValue property comes from one property of the MainView.
My code:
DataDisplayer.xaml
<UserControl x:Class="TabletMachineAccess.Controls.DataDisplayer"
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:local="clr-namespace:TabletMachineAccess.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="dataDisplayer"
MaxHeight="100"
mc:Ignorable="d">
<Border Height="auto"
Margin="5"
Padding="5,2,5,2"
BorderBrush="Gray"
BorderThickness="2"
CornerRadius="5">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding DataType}" />
<TextBlock Text=":" />
</StackPanel>
<Separator Style="{StaticResource BasicSeparator}" />
<TextBlock Margin="0,0,0,0"
HorizontalAlignment="Center"
FontWeight="Bold"
Text="{Binding Path=DataValue,
ElementName=dataDisplayer}" />
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<TextBlock FontWeight="ExtraLight" Text="(" />
<TextBlock FontWeight="ExtraLight" Text="{Binding UnitText}" />
<TextBlock FontWeight="ExtraLight" Text=")" />
</StackPanel>
</StackPanel>
</Border>
</UserControl>
DataDisplayer.xaml.cs
public partial class DataDisplayer : UserControl
{
public double DataValue
{
get { return (double)GetValue(DataValueProperty); }
set { SetValue(DataValueProperty, value); }
}
// Using a DependencyProperty as the backing store for DataValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataValueProperty =
DependencyProperty.Register("DataValue", typeof(double), typeof(DataDisplayer), new PropertyMetadata(9.9));
public string DataType
{
get; set;
}
public string UnitText
{
get; set;
}
public DataDisplayer()
{
InitializeComponent();
this.DataContext = this;
}
}
I try to use it like this in MainView.xaml
<controls:DataDisplayer Grid.Row="1"
Grid.Column="0"
DataType="{x:Static res:Strings.PVUpper}"
DataValue="{Binding PVUpper}"
UnitText="{x:Static res:Strings.KgUnit}" />
I have this in my MainViewModel.cs
public double PVUpper { get; set; }
For those properties, which get data from the res:Strings works fine and display the correct values, but DataValue property always shows the default value of the DependencyProperty (9.9). The value of PVUpper changes in every second in the ViewModel so I would like to see this change on the View as well.
The Problem is the wrong DataContex for the Binding. You set the DataContext-Property of your DataDisplayer to itself
public DataDisplayer()
{
InitializeComponent();
this.DataContext = this;
}
So the Binding DataValue="{Binding PVUpper}" tries to find a property with the name PVUpper on your DataDisplayer instance, which of course dose not succeed.
So you should not do this.DataContext = this; and just leave the DataContext untouched (your control will inherit the datacontext of its parent).
You also will need to change the bindings in your DataDisplayer.xaml to address the changed DataContext.
But you can simplay add an ElementName=dataDisplayer to all the bindings to bind against your DataDisplayer instance.
So for example
<TextBlock FontWeight="ExtraLight" Text="{Binding UnitText}" />
becomes
<TextBlock FontWeight="ExtraLight" Text="{Binding UnitText, ElementName=dataDisplayer}" />

Databinding and DataContext in WPF Desktop application with multiple views using UserControls

I am working on my first project with WPF and as practice, have created an application that has a menu bar with two buttons on the side which are used to change the display two different views on the grid to its right. I am using the MVVM pattern. Using the ICommand interface, I am able to change the view in the grid using usercontrols for the two views from the menu bar buttons. The application is as follows:
The menu bar has buttons named View 1 and View 2, clicking any of them will open up the relevant view in the usercontrol. At startup a "defaultview" is displayed. The default view has a button which can also be used to move to View 1. Below is what I am stuck at:
I have a button in View 1 to change the display to View 2 (it calls the same command I made in the App View Model but even though it changes the variable value to which the sidegrid is bound too, the view displayed doesnt change).
I want to pass data (tried with First Name) from View 1 to View 2 but I cant get binding to work.
Can someone guide me or link me to examples or material that discuss this?
App View Model
using DataBindingAndViewsTestProject.Views;
using Hotel_Management_Project_WPF.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DataBindingAndViewsTestProject.ViewModels
{
public class AppVM : ObservableObject
{
//Create a property that controls current view
private object _currentView;
public object CurrentView
{
get { return _currentView; }
set { OnPropertyChanged(ref _currentView, value);
}
}
//Instantiate the relaycommands, we will need to instantiate relaycommand objects for every command we need to perform.
//This means that we will need to do this for preses of all buttons
public RelayCommand View1ButtonCommand { get; private set; }
public RelayCommand View2ButtonCommand { get; private set; }
public AppVM()
{
CurrentView = new DefaultVM();
View1ButtonCommand = new RelayCommand(ShowView1, AlwaysTrueCommand);
View2ButtonCommand = new RelayCommand(ShowView2, AlwaysTrueCommand);
}
public void ShowView1(object dummy)
{
CurrentView = new View1();
}
public void ShowView2(object dummy)
{
CurrentView = new View2();
}
public bool AlwaysTrueCommand(object dummy)
{
return true;
}
}
}
Main Window.xaml
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"></ColumnDefinition>
<ColumnDefinition Width="600"></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<Button x:Name="view1Button" Content="Go to View 1" Margin="10" Command="{Binding View1ButtonCommand}"></Button>
<Button x:Name="view2Button" Content="Go to View 2" Margin="10" Command="{Binding View2ButtonCommand}"></Button>
</StackPanel>
<Grid Grid.Column="1">
<ContentControl Content="{Binding CurrentView}"></ContentControl>
</Grid>
</Grid>
</Window>
View1.xaml
<UserControl x:Class="DataBindingAndViewsTestProject.Views.View1"
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:DataBindingAndViewsTestProject.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
xmlns:vm="clr-namespace:DataBindingAndViewsTestProject.ViewModels">
<UserControl.Resources>
<vm:AppVM x:Name="AppVMinView1" x:Key="AppVMinView1"></vm:AppVM>
</UserControl.Resources>
<UserControl.DataContext>
<vm:View1VM></vm:View1VM>
</UserControl.DataContext>
<Grid Background="Aqua">
<StackPanel Margin="100">
<TextBlock Text="First Name"/>
<TextBox x:Name="firstNameTextBoxView1" Text="{Binding View1InfoClass.FirstName, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<TextBlock Text="Last Name"/>
<TextBox x:Name="lastNameTextBoxView1" Text="{Binding View1InfoClass.LastName, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<TextBlock Text="Random Useless Number" ></TextBlock>
<TextBox x:Name="randomUselessNumberView1" Text="{Binding View1InfoClass.Number, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<TextBlock Text="First Name Entered"></TextBlock>
<TextBlock Text="{Binding View1InfoClass.FirstName}"></TextBlock>
<TextBlock Text="Last Name Entered" ></TextBlock>
<TextBlock Text="{Binding View1InfoClass.LastName}"></TextBlock>
<TextBlock Text="Random Useless Number Entered"></TextBlock>
<TextBlock Text="{Binding View1InfoClass.Number}"></TextBlock>
<Button DataContext="{StaticResource AppVMinView1}" Content="Go to view2" Height="20" Width="70" Command="{Binding View2ButtonCommand}" />
</StackPanel>
</Grid>
</UserControl>
View2.xaml
<UserControl x:Class="DataBindingAndViewsTestProject.Views.View2"
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:DataBindingAndViewsTestProject.Views"
xmlns:vm="clr-namespace:DataBindingAndViewsTestProject.ViewModels"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<vm:View1VM x:Key="View1VMInView2" x:Name="View1VMInView2"></vm:View1VM>
</UserControl.Resources>
<UserControl.DataContext>
<vm:View2VM></vm:View2VM>
</UserControl.DataContext>
<Grid Background="Beige">
<StackPanel Margin="100">
<TextBlock Text="First Name"/>
<TextBox x:Name="firstNameTextBoxView2" Text="{Binding View2InfoClass.FirstName, Mode=OneWayToSource,UpdateSourceTrigger=PropertyChanged}"></TextBox>
<TextBlock Text="Last Name"/>
<TextBox x:Name="lastNameTextBoxView2" Text="{Binding View2InfoClass.LastName, Mode=OneWayToSource,UpdateSourceTrigger=PropertyChanged}"></TextBox>
<TextBlock Text="Random Useless Number"></TextBlock>
<TextBox x:Name="randomUselessNumberView2" Text="{Binding View2InfoClass.Number, Mode=OneWayToSource,UpdateSourceTrigger=PropertyChanged}"></TextBox>
<TextBlock Text="First Name Entered"></TextBlock>
<TextBlock DataContext="View1InView2" Text="{Binding View1InfoClass.FirstName}" ></TextBlock>
<TextBlock Text="Last Name Entered"></TextBlock>
<TextBlock></TextBlock>
<TextBlock Text="Random Useless Number Entered"></TextBlock>
<TextBlock ></TextBlock>
</StackPanel>
</Grid>
</UserControl>
Model (if relevant)
public class InfoClass:ObservableObject
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set {
OnPropertyChanged(ref _firstName, value);
}
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set { OnPropertyChanged(ref _lastName, value); }
}
private int _number;
public int Number
{
get { return _number; }
set { OnPropertyChanged(ref _number, value); }
}
}

how to Hide buttons in usercontrol from main window?

I have a user control that have a multi buttons and In the application i use this user control on multi windows ,but i want to Collapsed (shown/hidden) some buttons if the user select in the application a window 1 and show same button if the user select in the application a window 2
UserControl
<Grid x:Name="girdBtuWidow" >
<StackPanel Orientation="Horizontal">
<Button Content="add" x:Name="add" Visibility="{Binding window1_Loaded}" x:FieldModifier="public" Height="50" Width="100" Margin="0" Click="add_click" />
<Button Content="show history" x:Name="Personal" Height="50" Width="100" Margin="0" />
<Button Content="Show Customer" x:Name="Customer" Height="50" Width="100" Margin="0" />
</StackPanel>
</Grid>
how to set the property (Visibility) of button in the User Control from the application window ?
You don't need to use a Window_Loaded Event here.
You need to expose a Visibility property for each of your buttons in your UserControls.
In your UserControl add a binding to each button for the Visibility property:
Visibility="{Binding AddButtonVisibility}"
Visibility="{Binding ShowHistoryButtonVisibility}"
Visibility="{Binding ShowCustomerButtonVisibility}"
Make sure you add a DataContext to your UserControl, I generally use Self:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
In your UserControl Code Behind add Dependency Properties for each of the Bindings above:
public Visibility AddButtonVisibility
{
get { return (Visibility)GetValue(AddButtonVisibilityProperty); }
set { SetValue(AddButtonVisibilityProperty, value); }
}
// Using a DependencyProperty as the backing store for AddButtonVisibility. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AddButtonVisibilityProperty =
DependencyProperty.Register("AddButtonVisibility", typeof(Visibility), typeof(UserControl1), new PropertyMetadata(Visibility.Visible));
public Visibility ShowHistoryButtonVisibility
{
get { return (Visibility)GetValue(ShowHistoryButtonVisibilityProperty); }
set { SetValue(ShowHistoryButtonVisibilityProperty, value); }
}
// Using a DependencyProperty as the backing store for ShowHistoryButtonVisibility. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ShowHistoryButtonVisibilityProperty =
DependencyProperty.Register("ShowHistoryButtonVisibility", typeof(Visibility), typeof(UserControl1), new PropertyMetadata(Visibility.Visible));
public Visibility ShowCustomerButtonVisibility
{
get { return (Visibility)GetValue(ShowCustomerButtonVisibilityProperty); }
set { SetValue(ShowCustomerButtonVisibilityProperty, value); }
}
// Using a DependencyProperty as the backing store for ShowCustomerButtonVisibility. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ShowCustomerButtonVisibilityProperty =
DependencyProperty.Register("ShowCustomerButtonVisibility", typeof(Visibility), typeof(UserControl1), new PropertyMetadata(Visibility.Visible));
In Visual Studio, There is a code snippet shortcut for Dependency Properties - type propdp and hit tab twice.
Now, to use the properties you have just created put the Usercontrol onto the relevant window:
<local:UserControl1 AddButtonVisibility="Collapsed" />
local is the project namespaces' alias - defined at the top of your Window. You can just drag and drop the UserControl onto your window, and it will do this for you. (You may need to rebuild in order to see your UserControls in your Toolbox.
You should now see your control with the Add Button collapsed.
For completeness sake, here is the XAML side of things:
UserControl Xaml:
<UserControl x:Class="WpfApplication2.UserControl1"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid x:Name="girdBtuWidow" >
<StackPanel Orientation="Horizontal">
<Button Content="Add" x:Name="add" Height="50" Width="100" Margin="0" Click="Add_Click" Visibility="{Binding AddButtonVisibility}"/>
<Button Content="Show History" x:Name="Personal" Height="50" Width="100" Margin="0" Click="ShowHistory_Click" Visibility="{Binding ShowHistoryButtonVisibility}" />
<Button Content="Show Customer" x:Name="Customer" Height="50" Width="100" Margin="0" Click="ShowCustomer_Click" Visibility="{Binding ShowCustomerButtonVisibility}"/>
</StackPanel>
</Grid>
Window1.Xaml:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2" x:Class="WpfApplication2.Window1"
Title="Window1" Height="300" Width="308">
<Grid>
<local:UserControl1 HorizontalAlignment="Left" VerticalAlignment="Top" AddButtonVisibility="Collapsed" />
</Grid>
public static readonly DependencyProperty onBackVisibilityProperty =
DependencyProperty.Register("onBackVisibility", typeof(Visibility), typeof(MyToolBar), new PropertyMetadata(Visibility.Visible));
public Visibility onBackVisibility
{
get { return (Visibility)GetValue(onBackVisibilityProperty); }
set { SetValue(onBackVisibilityProperty, value); }
}
public static readonly DependencyProperty onBackVisibilityProperty =
DependencyProperty.Register("onBackVisibility", typeof(Visibility), typeof(MyToolBar), new PropertyMetadata(Visibility.Visible));
public Visibility onBackVisibility
{
get { return (Visibility)GetValue(onBackVisibilityProperty); }
set { SetValue(onBackVisibilityProperty, value); }
}
//After this goto xaml
Button Name="tbrBack"
ToolTip="{DynamicResource Back}"
VerticalAlignment="Center"
VerticalContentAlignment="Center"
Click="tbrBack_Click"
Visibility="{Binding onBackVisibility ,ElementName = usercontrol}

How to bind a property in the view model of a usercontrol to a property in another usercontrol

I am currently working within a WPF user control using MVVM. My MainWindow.xaml looks like below.
MainWindow.xaml
<Window.Resources>
<ObjectDataProvider x:Key="TabsList" ObjectType="{x:Type local:MainWindowModel}" MethodName="GetTabs"/>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox Grid.Column="0" ItemsSource="{Binding Source={StaticResource TabsList}}" IsSynchronizedWithCurrentItem="True">
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Path=TabName}" Margin="10"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ContentControl Grid.Column="1" Content="{Binding Source={StaticResource TabsList}, Path=MyUserControl}"/>
</Grid>
Data provider class is as below
public class MainWindowModel
{
public List<TabInfo> GetTabs()
{
return new List<TabInfo>()
{
new TabInfo() { TabName="Tab1", MyUserControl = new UserControl1()},
new TabInfo() { TabName="Tab2", MyUserControl = new UserControl2()}
};
}
}
public class TabInfo
{
public string TabName { get; set; }
public UserControl MyUserControl { get; set; }
}
And now I have two usercontrols UserControl1 and UserControl2 each has a text box control. I would like to update the Text property of the textbox control in the UserControl1 whenever the Text property of the Textbox control in the UserControl2 is updated. To do this, I tried as below.
UserControl1
<UserControl x:Class="MoreOnBinding2.UserControl1"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBox Width="200" Height="20" Text="{Binding Path=UserControl1Text}"/>
</Grid>
UserControl1ViewModel
public class UserControl1VM : ViewModelBase
{
public UserControl1VM()
{
this.UserControl1Text = "10";
}
private string userControl1Text;
public string UserControl1Text
{
get { return userControl1Text; }
set
{
if (userControl1Text != value)
{
userControl1Text = value;
RaisePropertyChanged(() => UserControl1Text);
}
}
}
}
UserControl2
<UserControl x:Class="MoreOnBinding2.UserControl2"
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:MoreOnBinding2"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBox Width="200" Height="20"
Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:UserControl1}},
UpdateSourceTrigger=PropertyChanged, Path=UserControl1Text}" />
</Grid>
But it is not working. It seems there is a problem in the RelativeSource tag in the UserControl2. Can someone help on this.
You could use a Dependency Property on your user controls this would give you a bind able property. see link
EDIT 1
Just had another look at your class setup. If you are following the MVVM approach I don't think you should have a reference to your user control in your view model
View Model should be more Like
public Class
{
ObservableCollection<TabInfo> _Tabs = new ObservableCollection<TabInfo>();
public ObservableCollection<TabInfo> Tabs
{
get{return _Tabs;}
set {_Tabs = value;}//Need to implement INotifyPropertyChanged [Link][2]
}
public TabInfo SelectedTab {get;set;}
}
public class TabInfo
{
public string TabName { get; set; }
public UserControlViewModel MyUserControlViewModel{ get; set; }
}
Then in your View
<Window.DataContext>
<vm:MainWindowModel />
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox Grid.Column="0" ItemsSource="{Binding Tabs}" IsSynchronizedWithCurrentItem="True" SelectedValue="{Binding SelectedTab}">
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Path=TabName}" Margin="10"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ContentControl Grid.Column="1" Content="{Binding SelectedTab.MyUserControlViewModel"/>
</Grid>
This should give you a list of Tabs and then when one is selected it will set the content of the ContentControl to the MyUserControlViewModel.
You would then need to use some sort of template selector to control the ContentTemplate to load different UserControls into the ContentControl
I haven't tested the code works and you would need to implement the INotifyPropertyChanged on all the public properties so the bindings update as the properties value is changed.
Hope that helps a bit.

DataTemplate and ContentControl when user class as DataContext

What I have:
User class
public class MyButton
{
public String ButtonProperty { get; set; }
public String LabelProperty { get; set; }
public MyButton()
{
ButtonProperty = "MyButtonText!";
LabelProperty = "LabelText!";
}
}
DataTemplate defined in window resources
<Window.Resources>
<DataTemplate DataType="{x:Type local:MyButton}">
<Border Width="100" Height="100" BorderThickness="2" BorderBrush="Aquamarine">
<StackPanel >
<Button>
<TextBlock Text="{Binding ButtonProperty}"></TextBlock>
</Button>
<Label Content="{Binding LabelProperty}"></Label>
</StackPanel>
</Border>
</DataTemplate>
</Window.Resources>
I want to DataTemplate will draw instead of instance of MyButton class
<Window x:Class="WpfApplication7.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication7"
Title="MainWindow" Height="500" Width="800">
<Window.Resources>
<DataTemplate DataType="{x:Type local:MyButton}">
<Border Width="100" Height="100" BorderThickness="2" BorderBrush="Aquamarine">
<StackPanel >
<Button>
<TextBlock Text="{Binding ButtonProperty}">
</TextBlock>
</Button>
<Label Content="{Binding LabelProperty}">
</Label>
</StackPanel>
</Border>
</DataTemplate>
</Window.Resources>
<!-- Create instance of MyButton in XAML-->
<local:MyButton></local:MyButton>
</Window>
It works fine, but it is not what I want at the end. What if instance of MyButton will DataContext for Window?
public MainWindow()
{
//Set instance of MyButton as DataContext
DataContext = new MyButton();
InitializeComponent();
}
I thought I must write that in XAML-side
<ContentControl DataContext="{Binding}">
<!--MyButton XAML code from DataTemplate here -->
</ContentControl>
instead of
<local:MyButton></local:MyButton>
but it doesn't work at all. what I am doing wrong?
You should try to bind to the Content property of your ContentControl instead of the DataContext property :
<ContentControl Content={Binding } />
Besides, the DataContext of the ContentControl is already the MyButton.
I'm not really sure what are you trying to achieve there. IF you simply want to extend the functionality of the default Button you could define attached properties
Why would you want the DataContext of your Window to be the Button? Maybe the other way around? Not sure I understood that part correctly.

Categories