How to move the DataContext definition from code-behind to XAML? - c#

I defined an object which contains properties in code-behind and, want to set the object to datacontext in xaml.
namespace WpfApplication4
{
public partial class MainWindow : Window
{
public EquipInfo equip1;
public MainWindow()
{
InitializeComponent();
equip1 = new EquipInfo();
// setting here works fine but i want to do in xaml
//textBox1.DataContext = equip1;
}
}
}
here's xaml code..
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox Text="{Binding Path=PIN}" Height="23"
HorizontalAlignment="Left" Margin="172,208,0,0"
Name="textBox1" VerticalAlignment="Top" Width="120" />
</Grid>
</Window>

You can set the DataContext via XAML to a public property. blindmeis has given a good example on how to do this.
What you did in your Post was not setting the DataContext, but binding the Text Property of your Control to a Property of the window. This only works if a DataContext is set. Another way to do that, if you don't want to do MVVM (which I'd recommend, though), is referencing specific Elements in your Binding:
1.) Alter member to AutoProperty or similar
public EquipInfo equip1 {get;set;}
2.) Give your window a name
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="myWin"
Title="MainWindow" Height="350" Width="525">
2.) Access Window Properties by instance name
<TextBox Text="{Binding ElementName=myWin, Path=equip1.PIN}"/>
On a side note, it would really be better to use a MVVM instead.

when you create your equipinfo class in codebehind why not setting the datacontext there?
nevertheless you can create a datacontext in xaml (you did not need the codebehind stuff then)
<TextBox Text="{Binding Path=PIN}">
<TextBox.DataContext>
<local:equip1/>
</TextBox.DataContext>
</TextBox>
but please look at the MVVM pattern

When you create your equipinfo class in codebehind why not setting the datacontext there?
The whole point of MVVM is to seperate UI from code behind using a viewmodel proxy. If you are going to reference the ui element to set its datacontext you might just as well reference it to set its value and forget about mvvm.

Related

How do I reference a custom property value in a WPF UserControl via Binding?

I'm building a WPF app with custom UserControls, and I'm trying to understand how property bindings are supposed to work. I can't get even the most basic binding to work, and it's simple enough to distill into a tiny example, so I figured someone with more WPF experience might be able to put me on the right track.
I've defined a custom UserControl called TestControl, which exposes a Foo property, which is intended to be set in XAML whenever a UserControl is placed.
TestControl.xaml.cs
using System.Windows;
using System.Windows.Controls;
namespace BindingTest
{
public partial class TestControl : UserControl
{
public static readonly DependencyProperty FooProperty = DependencyProperty.Register("Foo", typeof(string), typeof(TestControl));
public string Foo
{
get { return (string)GetValue(FooProperty); }
set { SetValue(FooProperty, value); }
}
public TestControl()
{
InitializeComponent();
}
}
}
The markup for TestControl just defines it as a control with a single button, whose label text displays the current value of the Foo property:
TestControl.xaml
<UserControl x:Class="BindingTest.TestControl"
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:BindingTest"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Button Content="{Binding Foo}" />
</Grid>
</UserControl>
In my MainWindow class, I just place a single instance of TestControl with its Foo property set to "Hello".
MainWindow.xaml
<Window x:Class="BindingTest.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:BindingTest"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<local:TestControl Foo="Hello" />
</Grid>
</Window>
I would expect that when I build and launch this app, I'd see a window with a single button reading "Hello". However, the button is blank: the Binding doesn't seem to work.
If I add a click handler to the TestControl's button, I can verify that the value is being updated behind the scenes:
// Added to TestControl.xaml.cs:
private void Button_Click(object sender, RoutedEventArgs e)
{
Console.WriteLine("Button clicked; Foo is '{0}'", Foo);
}
// Updated in TestControl.xaml:
// <Button Content="{Binding Foo}" Click="Button_Click" />
When I click the button, I get Button clicked; Foo is 'Hello', but the GUI never updates. I've tried using Path=Foo, XPath=Foo, etc., as well as setting UpdateSourceTrigger=PropertyChanged and verifying updates with NotifyOnTargetUpdated=True... nothing seems to result in the text in the UI being updated to match the underlying property value, even though the property value seems to be getting updated just fine.
What am I doing wrong? I feel like there's just a simple and fundamental misunderstanding in how I'm approaching this.
edit:
Poking around a bit more and reading similar questions has led me to a potential fix: namely, adding a name to the root UserControl element in TestControl.xaml (x:Name="control"), and changing the binding to explicitly specify that control ({Binding Foo, ElementName=control}).
I'm guessing that by default, {Binding Foo} on the Button element just means "find a property named 'Foo' on this Button control", whereas I'd assumed it'd mean "find a property named 'Foo' in the context that this Button is being declared in, i.e. on the TestControl".
Is specifying an explicit ElementName the best fix here?
You have to set the source object of the Binding to the UserControl instance, e.g. like this:
<Button Content="{Binding Foo, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
or
<UserControl ... x:Name="theControl">
...
<Button Content="{Binding Foo, ElementName=theControl}"/>
If you have many such Bindings, you may also set the DataContext of the top level element in the UserControl's XAML to the UserControl instance:
<Grid DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}">
<Button Content="{Binding Foo}" />
<Button Content="{Binding Bar}" />
</Grid>
You must however avoid to set the DataContext of the UserControl (which is often recommend by "expert" bloggers), because that would break DataContext-based Bindings of the UserControl properties like
<local:TestControl Foo="{Binding SomeFoo}" />

Cannot display binding on usercontrol?

I'm not able to display a property value on the usercontrol.
I set up the datacontext in this way:
public MainController cm;
public static MainWindow AppWindow;
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
cm = new MainController();
DataContext = cm;
AppWindow = this;
}
}
inside MainController I've all the controller with all the properties like this:
public class MainController: MainControllerVM
{
private ClubController _clubController = new ClubController();
public ClubController ClubController
{
get { return _clubController ; }
}
}
Now I've splitted my user interface in different controls to have more xaml organization. I need to access to the main datacontext that's cm from all user controls, I tried in this way:
public partial class Club : UserControl
{
public Club ()
{
InitializeComponent();
DataContext = MainWindow.AppWindow.cm;
}
but I get:
NullReferenceException
on AppWindow. My main problem's that I can't get to display the value of the property on a label available on the user control:
<Label Content="{Binding ClubController.Club.Name}" />
this binding working in the main window but not working on usercontrol, why??
Suppose you have a window like this:
<Window x:Class="Example.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Example"
Title="MainWindow" Height="350" Width="525">
<UniformGrid Rows="2" Columns="2">
<local:MyUserControlA/>
<local:MyUserControlB/>
<local:MyUserControlC/>
<local:MyUserControlD/>
</UniformGrid>
</Window>
And you set the DataContext in the constructor:
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
Now remember that the DataContext is an inheritable dependency property, i.e. it flows down. (In general, dependency properties are not inheritable by default, unless you explicitly state it)
So, you set the DataContext once on the root of the logical tree (the window) and all of its children will "see" it. (the UniformGrid and the custom controls in our case)
Yes, that means you can directly bind to the view model in your user control's XAML:
<UserControl x:Class="Example.MyUserControlA"
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>
<TextBlock Text="{Binding PropertyFromMainViewModel}"/>
</Grid>
</UserControl>
Now, this approach works well, until your control gets so complicated that it needs to have its own ViewModel and DataContext reespectively.
Usually this happens when the control is not a passive, but maintains a state (validates input, button state, etc.)
1.Declare all properties that you want to bind to the main view model as dependency properties and pay attention to the default value you specify.
2.Locate the main panel of your UserControl and name it, for example "LayoutRoot":
<UserControl x:Class="Example.MyUserControlA"
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 x:Name="LayoutRoot">
<TextBlock Text="{Binding MyDependencyProperty}"/>
</Grid>
</UserControl>
3.Now, you set the DataContext on the LayoutRoot
public MyUserControlA()
{
InitializeComponent();
LayoutRoot.DataContext = new MyUserControlViewModel();
}
4.You bind to the main view model in this way
<Window x:Class="Example.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Example"
Title="MainWindow" Height="350" Width="525">
<UniformGrid Rows="2" Columns="2">
<local:MyUserControlA MyDependencyProperty="{Binding MainViewModelProperty}"/>
<local:MyUserControlB/>
<local:MyUserControlC/>
<local:MyUserControlD/>
</UniformGrid>
</Window>
The other way around is to bind using RelativeSource, but this would break the encapsulation and reusability of your UserControl.
WPF has a steep learning curve, I hope my tips were helpful...

Problems with binding a static ObservableCollection

I have a class (very little code for testing purposes) which contains a static ObservableCollection, which gets populated from elsewhere:
public class TestClass
{
public static ObservableCollection<int> TestCollection = new ObservableCollection<int>();
}
... and a basic WPF window with a ListBox:
<Window x:Class="app.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListBox x:Name="list"/>
</Grid>
</Window>
When I tried binding programmatically:
list.ItemsSource = Containers.TestClass.TestCollection;
... it worked just fine. However, when I try to perform binding through XAML:
<Window x:Class="app.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="clr-namespace:app.Containers"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<d:TestClass x:Key="dataSource"/>
</Window.Resources>
<Grid>
<ListBox x:Name="list" ItemsSource="{Binding Source={StaticResource dataSource}, Path=TestCollection}"/>
</Grid>
</Window>
... nothing gets displayed.
I've also tried setting DataContext:
<Window.Resources>
<l:LifeEngine x:Key="dataSource"/>
</Window.Resources>
<Window.DataContext>
<Binding Source="{StaticResource dataSource}"/>
</Window.DataContext>
...and using path...
...and nothing gets displayed once again.
Also, not sure if it matters, but when I make my class static I get an error in my XAML code saying:
The type 'TestClass' is abstract and must include an explicit value.
So, that's out of the question.
Any idea how I can bind that ObservableCollection through XAML?
Problem is you are trying to find static property on an instance object.
<ListBox ItemsSource="{Binding Source={StaticResource dataSource},
Path=TestCollection}"/>
Here dataSource is pointing to an instance of TestClass and by binding Path to TestCollection, you are asking to bind to an instance property TestCollection which is static instead. That's why it's not working.
You have to use x:Static markup extension to bind to static properties. (Note you are not creating any instance object of TestClass)
<ListBox ItemsSource="{Binding Source={x:Static d:TestClass.TestCollection}}"/>
Also be informed that static properties binds one time. If you change the instance at runtime that won't be reflected back on UI. In your case you are dealing with ObservableCollection so when you add/delete item from collection it will be refreshed on UI but in case you reinitialize the list that change won't be reflected back on UI. In case you want to update UI, you have to raise StaticPropertyChangedEvent. In case interested, check out my answer here.

Access to Usercontrols in WPF

I'm just starting out with WPF having used WinForms for some time and seem to have fallen at the first hurdle.
I have my main XAMLdefined as
<Window x:Class="FHIRCDALoader.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:FHIRCDALoader.xaml"
Title="FHIR CDA Loader" Height="350" Width="525"
Icon="Icons/color_swatch.png">
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.New"
Executed="NewDocument" />
</Window.CommandBindings>
<DockPanel>
<local:menubar DockPanel.Dock="Top"/>
<local:toolbar DockPanel.Dock="Top"/>
<local:statusbar DockPanel.Dock="Bottom" />
<RichTextBox x:Name="Body"/>
</DockPanel>
</Window>
Note the use of the user controls, one of which is the "statusbar"
<UserControl x:Class="FHIRCDALoader.xaml.statusbar"
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">
<StatusBar >
<StatusBarItem>
<TextBlock x:Name="bbstatusbar" />
</StatusBarItem>
</StatusBar>
</UserControl>
So in MainWindow.xaml.cs I see I can reference RichTextBox named body from the main XAML file. I can't however reference the TextBlock in the UserControl which is named "bbstatusbar".
How do I set the value of the TextBlock from MainWindow.xaml.cs?
In agreement with Vlad and HighCore's comments: you don't set the TextBlock from MainWindow.xaml.cs. You bind it to a view-model. A binding simply looks like this:
<TextBlock Text="{Binding StatusText}" />
The above says: bind the Text property to a property in the current data-context called "StatusText". Next, create a view model:
public class ViewModel : INotifyPropertyChanged
{
public string StatusText
{
get { return _statusText; }
set
{
_statusText = value;
RaisePropertyChanged("StatusText");
}
}
// TODO implement INotifyPropertyChanged
}
Finally, set the DataContext of your MainPage to the view model. You can do this in a variety of ways, but let's say here for simplicity, do it in the constructor:
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel { StatusText = "hello world" };
}
Now, the idea is to put your model-related logic into ViewModel. So, you shouldn't need to access the UI elements -- instead, update the view-model properties that the UI elements are bound to.

How to bind WPF TextBox to a propperty that is the member of application main window

I have a private field
private static Double myValue;
in the application MainWindow class. And there (in the MainWindow class) I defined a property
public static Double MytValue
{
get { return myValue; }
}
In the structure of the MainWindow class I have a TextBox. I'm in need of binding it to the MytValue property. In XAML I write:
<TextBox Name="tbxMyValue" Grid.Row="1" Grid.Column="2" TextAlignment="Center"
Text="{Binding Path=MyValue}" Width="Auto" Margin="10,0,10,15" IsEnabled="True" />
But it has no effect. I see nothing in the TextBox while myValue variable has a value. Why? Please help me.
I like to set the DataContext in the Window section
<Window x:Class="Gabe3a.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Gabe3a"
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
xmlns:System="clr-namespace:System;assembly=mscorlib"
DataContext="{Binding RelativeSource={RelativeSource self}}"
Title="Gabriel Main Ver 3a01" Icon="faviconw.ico" Height="600" Width="800">
You need to set the DataContext behind the Window for that binding to work
There are two layers to an application with WPF: the UI layer and the data layer.
The Data layer for an application starts out as null, and you can set it using the DataContext property.
Whenever you do a basic binding in WPF, you are binding to the DataContext. So Text="{Binding Path=MyValue}" is actually saying "Get the MyValue property from the current DataContext".
You could simply set the DataContext in your code behind:
MyWindow.DataContext = this;
Or you can use a RelativeSource binding to tell WPF to get the MyValue property from somewhere else, such as telling it to get it from the closest Window it finds when navigating up the VisualTree:
Text="{Binding Path=MyValue, RelativeSource={
RelativeSource AncestorType={x:Type Window}}}"
I actually have an article on my blog about the DataContext that I'd recommend reading if you're new to WPF and the DataContext: What is this "DataContext" you speak of?
It's not how it works. Binding to static properties; {Binding Source={x:Static local:Application.MyValue}}
Note that your field needs to be property & public. If you want to go with your solution, you need to set DataContext as {RelativeSource Self}.
In file MainWindow.xaml.cs in constructor add this line:
DataContext = this;
after InitializeComponent();

Categories

Resources