Set Window.DataContext to a declared property in the code-behind? - wpf

I want to achieve something like the following (Notice the DataContext property of the Window element):
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding MyDataContext}"/>
Class Window1
Public ReadOnly Property MyDataContext() As IEnumerable(Of String)
Get
Return New String() {"Item1", "Item2"}
End Get
End Property
End Class

<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding MyDataContext, RelativeSource={RelativeSource Self}}">
<Grid>
<ListBox ItemsSource="{Binding}"/>
</Grid>
</Window>
I think it might be better to use a DependencyProperty, it should synchronize well.

Related

How to set Data or Communicate with Property in UserControl As New Window?

I just Started with WPF MVVM but i stuck on a point. I want to define the problem as Point.
I created a UserControl with one ComboBox(Binded with "Data" Property) and One Button (UserControl1.xaml).
Added to UserControl1 to MainWindow.xaml in Grid As
Created a Class1.vb With Public Property with INOtifyPropertyChange
Binded using
Binded Button with ICommand.
Everything is Working as button is working , combobox showing Data Properly but....
I added another UserControl As UserControl2.Xaml
Added another ComboBox(binded with "Data1" property) in UserControl2
UserControl2 is also binded with Class1
On Button i am setting two things
i) Setting Data1 = Data (So value of Data set to Data1 on button click)
ii) Initializing the UserControl2 As Window by
Dim win As New Window with {.Content = New UserControl2}
10) On button click from UserControl1 the Window is Showing but Data is not there which is binded on Usercontrol2 Combobox
I have tried dataContext on code behind in xaml.vb
MainWindow.xaml
<Window x:Class="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:WpfApplication3"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:UserControl1 ></local:UserControl1>
</Grid>
</Window>
UserControl1.Xaml :-
<UserControl x:Class="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"
xmlns:local="clr-namespace:WpfApplication3"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.DataContext >
<local:Class1 ></local:Class1>
</UserControl.DataContext>
<Grid>
<ComboBox x:Name="comboBox" HorizontalAlignment="Left" Margin="80,94,0,0" VerticalAlignment="Top" Width="120" ItemsSource="{Binding DataContext.Data,RelativeSource={RelativeSource AncestorType=UserControl } , UpdateSourceTrigger=PropertyChanged }"/>
<Button x:Name="button" Command="{Binding DataContext.TestBut ,RelativeSource={RelativeSource AncestorType=UserControl} , UpdateSourceTrigger=PropertyChanged }" Content="Button" HorizontalAlignment="Left" Margin="90,161,0,0" VerticalAlignment="Top" Width="75"/>
</Grid>
</UserControl>
Class1.vb
Imports System.ComponentModel
Public Class Class1
Implements INotifyPropertyChanged
Private _data As String()
Private _data1 As String()
Private _testBut As ICommand
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Sub OnPropertyChange(prop As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(prop))
End Sub
Public Property Data As String()
Get
Return _data
End Get
Set(value As String())
_data = value
OnPropertyChange("Data")
End Set
End Property
Public Property TestBut As ICommand
Get
Return New Command(AddressOf test)
End Get
Set(value As ICommand)
_testBut = value
OnPropertyChange("TestBut")
End Set
End Property
Public Property Data1 As String()
Get
Return _data1
End Get
Set(value As String())
_data1 = value
OnPropertyChange("Data1")
End Set
End Property
Private Sub test(obj As Object)
Data1 = Data
Dim win As New Window With {.Content = New UserControl2,
.DataContext = Data1}
win.Show()
End Sub
Public Sub New()
Data = New String() {"test", "test1", "test2", "test3"}
End Sub
End Class
UserControl2
<UserControl x:Class="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:WpfApplication3"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<User
<Grid>
<ComboBox x:Name="comboBox" HorizontalAlignment="Left" Margin="78,101,0,0" VerticalAlignment="Top" Width="120" ItemsSource="{Binding DataContext.Data1, RelativeSource={RelativeSource AncestorType=UserControl}, UpdateSourceTrigger=PropertyChanged }"/>
</Grid>
</UserControl>
I want to minimize the code in code behind (xaml.vb) as much possible as i want no code in xaml.vb, also I want to set communication between two usercontrol
i hope you can enlighten me on this problem
If you want to use the Same Data in both usercontrol why cant you bind same "Data" instead of binding "Data1" in UserControl2?
Please see the modification. I have assigned datacontext in UserControl2.xaml. And changed combobox itemsource binding to Data
<UserControl x:Class="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:WpfApplication3"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.DataContext >
<local:Class1 ></local:Class1>
</UserControl.DataContext>
<Grid>
<ComboBox x:Name="comboBox" HorizontalAlignment="Left" Margin="78,101,0,0" VerticalAlignment="Top" Width="120" ItemsSource="{Binding DataContext.Data, RelativeSource={RelativeSource AncestorType=UserControl}, UpdateSourceTrigger=PropertyChanged }"/>
</Grid>
</UserControl>
In the Function Test, i removed setting datacontext as we are already setting in xaml
Private Function test()
Dim win As New Window With {.Content = New UserControl2
}
win.Show()
End Function
I am able to populate combo box in usercontrol 2 .
Edit : Problem why you are not able to see the data with assigning "Data1", once you create the new usercontrol , new instance of view model will be created and you are assigning "Data1=Data" before creating new window instance .
I have modified the code as per your suggestion .
Private Function test()
Dim customer3 As Class1
customer3 = New Class1()
Dim win As New Window With {.Content = New UserControl2, .DataContext = customer3
}
Dim classObject = CType(win.DataContext, Class1)
classObject.Data1 = Data
win.Show()
End Function
Xaml will be using "Data1" object as itemsource
<UserControl x:Class="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:WpfApplication3"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<ComboBox x:Name="comboBox" HorizontalAlignment="Left" Margin="78,101,0,0" VerticalAlignment="Top" Width="120" ItemsSource="{Binding DataContext.Data1, RelativeSource={RelativeSource AncestorType=UserControl}, UpdateSourceTrigger=PropertyChanged }"/>
</Grid>
</UserControl>
Please ignore poor naming

Accessing dependency property in the same control xaml

In a Wpf Application i have a main window.
I have added a user control to the same project.
In the user control's .xaml.cs file a Dependency property ( "Value" name of the property ) is added.
I would like to access the defined dependency property in the usercontrol.xaml.
I know i can do the same while creating the control instance either in window.xaml or some other user control.
But is it possible to access the dependency property defined in .xaml.cs in .xaml?
Question updated based on Vivs answer
Ok. I mentioned my question wrongly. Nevertheless even i was not aware of accessing. But my actual intended question is it possible to set the dependency property from .xaml. some thing like from the example given above,
<Grid CustomBackground ="{Binding Path= BackgroundColor}" />
Or
<Grid CustomBackground ="Blue" />
Is it possible to set the custom dependency properties like this in the same .xaml?
Yes it is possible.
something like:
.xaml
<UserControl x:Class="MvvmLight26.UserControl1"
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:MvvmLight26"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:UserControl1}}, Path=CustomBackground}" />
</UserControl>
and .xaml.cs:
public partial class UserControl1 : UserControl {
public static readonly DependencyProperty CustomBackgroundProperty =
DependencyProperty.Register(
"CustomBackground",
typeof(Brush),
typeof(UserControl1),
new FrameworkPropertyMetadata(Brushes.Tomato));
public UserControl1() {
InitializeComponent();
}
public Brush CustomBackground {
get {
return (Brush)GetValue(CustomBackgroundProperty);
}
set {
SetValue(CustomBackgroundProperty, value);
}
}
}
Alternate:
If you say have the DataContext of the UserControl as itself like:
public UserControl1() {
InitializeComponent();
DataContext = this;
}
then in your xaml you could just go with:
<Grid Background="{Binding Path=DataContext.CustomBackground}" />
Update:
For the new question,
Not quite directly.
You can "set" the value if the custom DP is registered as an attached property(Do remember an attached property is not the same as a normal DP in it's behavior and scope.)
If you want to keep it as a normal DP, then you can keep UserControl1 from the original answer same as it is(just the DP part. You need to remove the xaml part of it and make it a non-partial class in the code-behind) and then derive it to a new UserControl.
something like:
<local:UserControl1 x:Class="MvvmLight26.UserControl2"
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:MvvmLight26"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
CustomBackground="Blue"
mc:Ignorable="d">
<Grid />
</local:UserControl1>
You can ofc name UserControl1 as something like "BaseUserControl" or so to make it obvious that it's not intended for direct usage.
You can set the value from the UserControl.Style in the same xaml as well.
xaml:
<UserControl x:Class="MvvmLight26.UserControl1"
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:MvvmLight26"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<UserControl.Style>
<Style>
<Setter Property="local:UserControl1.CustomBackground"
Value="Blue" />
</Style>
</UserControl.Style>
<Grid Background="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:UserControl1}}, Path=CustomBackground}" />
</UserControl>

WPF user control's datacontext to property in codebehind

Having a simple XAML user control, I'd like to set the DataContext to the code behind (xaml.cs) file.
I'd like to set DataContext and Itemssource in XAML, so I can populate the combobox with property ListOfCars
XAML
<UserControl x:Class="Sample.Controls.MyControl"
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="85" d:DesignWidth="200">
<Grid Height="85" Width="200" Background="{StaticResource MainContentBackgroundBrush}">
<StackPanel Orientation="Vertical">
<ComboBox Height="23.338" x:Name="CarList" />
</StackPanel>
</Grid>
</UserControl>
Code behind
public List<Cars> ListOfCars
{
get { return _store.ListCars(); }
}
In other words, instead of doing this in codebehind, how may I set the binding in XAML
public MyControl()
{
InitializeComponent();
_store = new Store();
CarList.ItemsSource = _store.ListCars();
CarList.DisplayMemberPath = "Name";
}
Just bind the ItemsSource.
<ComboBox ItemsSource="{Binding ListOfCars}"/>
And then for the UserControl:
<MyControl DataContext="{Binding *viewModel*}"/>
You have to bind the DataContext where your UserControl is used rather than in the definition, because in the definition you don't know to what to bind. The Combobox automatically is in the context of the control so you can just bind to the DataContext without any additional work.
Example of binding to a resource:
<Application.Resources>
...
<viewmodels:ViewModelLocator x:Key="ViewModelLocator"/>
...
</Application.Resources>
<MyControl DataContext="{Binding Source={StaticResource ViewModelLocator}}"/>
This creates an instance of the ViewModelLocator and then binds the DataContext of the control to that resource.
Do not do that, you will mess up all external bindings to the DataContext. Use UserControl.Name and ElementName bindings instead (or RelativeSource).

How to Expose a Control in XAML

I'm new in WPF and I'm creating a control. This control contains a DataGrid and some other WPF controls.
I created my Control as below:
<UserControl x:Class="MyControls.MyControl"
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="329" d:DesignWidth="535" >
<Grid>
<DataGrid Margin="6,25,6,35" Name="dataGrid" SelectionUnit="CellOrRowHeader" x:FieldModifier="public" HeadersVisibility="All"/>
<OtherControl HorizontalAlignment="Left" x:Name="otherControl" Height="34" VerticalAlignment="Bottom" Width="523" x:FieldModifier="private"/>
<Label Content="caption" Height="24" HorizontalAlignment="Left" Name="captionLabel" VerticalAlignment="Top" Foreground="#FF2626D1" x:FieldModifier="private"/>
</Grid>
</UserControl>
So, everything goes well so far, then I create a container UserControl which has in it my control created previously:
<UserControl x:Class="MyContainers.MyContainer"
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" xmlns:my="clr-namespace:MyControls">
<Grid>
<my:MyControl>
</my:MyControl>
</Grid>
What I can not do is the following:
<my:MyControl>
<my:MyControl.dataGrid>
</my:MyControl.dataGrid>
</my:MyControl>
I previously set the datagrid's property of FieldModifier as public in order to get access to it in another xaml, but it raises an error from visual studio.
I need to "expose" my dataGrid in order to be able to add columns and their styles.
I would like to be able to do something like this:
<my:MyControl.dataGrid.Columns >
<DataGridTextColumn />
<DataGridTextColumn />
...
<DataGridTextColumn />
</my:MyControl.dataGrid.Columns>
So, is not enough to set the datagrid's property of FieldModifier as public?
Do I need to do something else? How can I achieve this? Is this even possible?
I hope someone can help me. Thank you in advance.
You cannot access the child DataGrid as MyControl.dataGrid -- MyControl has no property named "dataGrid".
You might try adding a dependency property of type ObservableCollection<DataGridColumn> to MyControl, and modify the dataGrid columns whenever that collection changes.
EDIT:
Whipped together a quick example for you:
UserControl code:
public partial class UserControl1 : UserControl
{
public static readonly DependencyProperty ColumnsProperty = DependencyProperty.Register("Columns", typeof(ObservableCollection<DataGridColumn>), typeof(UserControl1));
public ObservableCollection<DataGridColumn> Columns
{
get { return (ObservableCollection<DataGridColumn>)GetValue(ColumnsProperty); }
set { SetValue(ColumnsProperty, value); }
}
public UserControl1()
{
Columns = new ObservableCollection<DataGridColumn>();
Columns.CollectionChanged += (s, a) =>
{
dataGrid.Columns.Clear();
foreach (var column in this.Columns)
dataGrid.Columns.Add(column);
};
InitializeComponent();
}
}
UserControl xaml:
<UserControl x:Class="WpfApplication1.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">
<Grid>
<DataGrid x:Name="dataGrid" AutoGenerateColumns="False"/>
</Grid>
so you can use it like:
<Grid>
<l:UserControl1>
<l:UserControl1.Columns>
<DataGridTextColumn Header="Col1"/>
<DataGridTextColumn Header="Col2"/>
</l:UserControl1.Columns>
</l:UserControl1>
</Grid>

DependencyProperty in UserControl doesn't get updated from binding

Here is the reproduction of my problem:
Create a WPF Application
Add a new UserControl to the project
Replace its content with the following
<UserControl
x:Class="UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<TextBlock Text="{Binding MyText}"/>
</UserControl>
Public Class UserControl1
Public Property MyText As String
Get
Return GetValue(MyTextProperty)
End Get
Set(ByVal value As String)
SetValue(MyTextProperty, value)
End Set
End Property
Public Shared ReadOnly MyTextProperty As DependencyProperty =
DependencyProperty.Register("MyText", GetType(String), GetType(UserControl1))
End Class
Replace the following in the MainWindow.xaml file:
<Window
x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:WpfApplication1"
Title="MainWindow"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<StackPanel >
<TextBox Text="{Binding Title, UpdateSourceTrigger=PropertyChanged}"/>
<src:UserControl1 MyText="{Binding Title}"/>
</StackPanel>
</Window>
As you can see, the UserControl1.MyText property doesn't get updated when the MainWindow.Title changes.
What am I missing? I want the user control to be connected to the parent property, is there a xamly way to do it?
Okay, when I recreate the app (I'm using C#, so I don't think this will make any difference), I notice in my output the binding is failing because UserControl does not have a Title property.
Add ElementName=this to your UserControl1 binding. And set the Name property on the Window to this and that should fix it.
Something like this:
<src:UserControl1 MyText="{Binding Title, ElementName=this}" />
The binding works for me when I do that. Hope that helps!

Resources