Simple WPF/XAML question. In XAML, how do I reference the Self/this object in a given context? In a very basic app with a main window, one control, and a coded C# property of the window, I want to bind a property of the control to the hand coded property of the window.
In code, this is very easy - in the Window's constructor, I added this:
Binding bind = new Binding();
bind.Source = this;
bind.Path = new PropertyPath("ButtonWidth");
button1.SetBinding(WidthProperty, bind);
Obviously, I have a property called ButtonWidth, and a control called button1. I can't figure out how to do this in XAML. Various attempts like the following example have not worked:
<Button x:Name="button1" Width="{Binding Source=Self Path=ButtonWidth}"/>
<Button x:Name="button1" Width="{Binding RelativeSource={RelativeSource Self} Path=ButtonWidth}"/>
etc
Thanks
First use a comma between the RelativeSource and Path in your Binding:
<Button x:Name="button1" Width="{Binding RelativeSource={RelativeSource Self},
Path=ButtonWidth}"/>
Secondly, the RelativeSource binds to the Button. Button has no property called ButtonWidth. I am guessing you need to Bind to your parent control.
So try this RelativeSource binding:
<Button x:Name="button1" Width="{Binding RelativeSource=
{RelativeSource FindAncestor, AncestorType={x:Type YourNamespace:YourParentControl}},
Path=ButtonWidth}"/>
I think what you are looking for is this:
<Window x:Class = "blah blah all the regular stuff"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
>
One way I get around having to deal with RelativeSource and the like is to name the root XAML element:
<Window x:Class="TestApp2.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"
x:Name="_this"
>
<Grid>
<Button x:Name="button" Width="{Binding ElementName=_this,Path=ButtonWidth}" />
</Grid>
</Window>
If you want to set the DataContext you could also do this:
<Window x:Class="TestApp2.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"
x:Name="_this"
>
<Grid DataContext="{Binding ElementName=_this}">
<Button x:Name="button" Width="{Binding Path=ButtonWidth}" />
</Grid>
</Window>
I find this to be a good trick to not have to remember all the complexities of the RelativeSource binding.
The problem with naming the XAML root element is that, if you get into the habit of using the same name (i.e, "_this", "Root", etc.) for all the roots in your project, then late-binding in nested templates may access the wrong element. This is because, when {Binding} ElementName=... is used in a Template, names are resolved at runtime by walking up the NameScope tree until the first match is found.
Clint's solution avoids naming the root element, but it sets the root element into its own DataContext, which might not be an option if the DataContext is needed for, say, data. It also seems a bit heavy-handed to introduce another binding on an element just for the purpose of providing access to it. Later, if access is no longer needed, that {Binding} will become clutter: responsibility for access properly belongs with the target and binding.
Accordingly, here is a simple markup extension to access the XAML root element without naming it:
using System.Xaml;
using System.Windows.Markup;
public sealed class XamlRootExtension : MarkupExtension
{
public override Object ProvideValue(IServiceProvider sp)
{
var rop = sp.GetService(typeof(IRootObjectProvider)) as IRootObjectProvider;
return rop == null ? null : rop.RootObject;
}
};
XAML:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:global="clr-namespace:">
<TextBlock Text="{Binding Source={global:XamlRoot},Mode=OneTime}" />
</Window>
note: for clarity, I didn't define the MarkupExtension in a namespace; using an empty clr-namespace alias as shown here d̲o̲e̲s̲ actually work in for accessing the global:: namespace (although the VS2013 designer seems to complains about it).
Result:
A window whose content is bound to itself.
n.b.
Unfortunately, naming the root element with "ElementName=.." seems to be the only way with UWP as {RelativeSource Self} is not supported there.
Oddly enough, this still works when the name is overrid in layout, e.g.
<UserControl x:Class="Path.MyClass" x:Name="internalName">
<Border Background={Binding Path=Background, ElementName=internalName}" ...
then
<Page>
<local:MyClass x:Name=externalName />
</Page>
BTW, Windows 10 fixed an error (present at Windows 8.1), when same internal name is used for different elements in same layout.
Still, I would prefer to use {RelativeSource Self}, since it appears more logical and safer to me.
Related
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}" />
I have a binding to a parent-element. How can I provide the data type for the DataContext in the binding, so intellisense can resolve the bound Properties?
The binding works fine at runtime. So, I have the following XAML structure:
<TabControl Name="TabDynamic"
ItemsSource="{Binding TabItems, Mode=OneWay}" ...>
<TabControl.Resources>
<DataTemplate x:Key="TabHeader" DataType="TabItem">
<DockPanel>
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=Header}" />
<Button Command="{Binding ElementName=TabDynamic, Path=DataContext.DeleteTabCommand}"
CommandParameter="{Binding ElementName=TabDynamic, Path=DataContext.TabItems/}">
<Image Source="{DynamicResource DeleteImg}" Height="11" Width="11"></Image>
</Button>
</DockPanel>
</DataTemplate>
<DataTemplate x:Key="TabContent" DataType="viewModels:ConnectionInfoVM">
<views:BufferViewerControl/>
</DataTemplate>
</TabControl.Resources>
</TabControl>
The data type of the DataContext is "viewModels:ConnectionInfoVM".
Intellisense will now underline both Properties on the DataContext (so DeleteTabCommand and TabItems are underlined).
I already tried to use the design-time data-context definition "d:DataContext" within the Button element like so:
d:DataContext="{d:DesignInstance viewModels:ConnectionInfosVM}"
But this does not change the intellisense warnings.
I also tried to define the DataType on the DataTemplate to be "viewModels:ConnectionInfosVM", as I do for the content-template, but that too does not change the intellisense warnings (and I guess would be wrong, as the data type of the element really is a TabItem).
Another try was to define the DataContext by adding the following to the Button element definition:
<Button.DataContext>
<viewModels:ConnectionInfosVM/>
</Button.DataContext>
But this too, does not get rid of the warnings.
I found a solution at least for the above described problem. I was setting the DataContext of the window in question in the code-behind. The TabControl in my example just inherits that DataContext, which is a ConnectionInfosVM.
In order to fix those warnings (and of course for the gained flexibility in providing a DataContext by a Locator), I defined the DataContext in XAML like so:
<UserControl ...
DataContext="{Binding Source={StaticResource mainViewModelLocator}, Path=ConnectionInfosVM}">
It seems that intellisense is now able to resolve those references. But on the other side, I also changed the TextBlock contained within the DataTemplate to the following:
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=DataContext.Address}" />
Now the DataContext of such a TabItem is actually a different type, as it is the type of the backing object for the TabItem. So in this case, the Property Address cannot be found by intellisense.
So, the question kind of remains, is there a possibility to define the type of the DataContext within a binding defined in a DataTemplate?
I needed to add the mc:Ignorable="d" attribute to the Window tag. Essentially I learned something new. The d: namespace prefix that Expression Blend/Visual Studio designer acknowledges is actually ignored/"commented out" by the real compiler/xaml parser!
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
The following was taken from
Nathan, Adam (2010-06-04). WPF 4 Unleashed (Kindle Locations 1799-1811). Sams. Kindle Edition.
Markup Compatibility
The markup compatibility XML namespace (http://schemas.openxmlformats.org/markup-compatibility/2006, typically used with an mc prefix) contains an Ignorable attribute that instructs XAML processors to ignore all elements/attributes in specified namespaces if they can’t be resolved to their .NET types/members. (The namespace also has a ProcessContent attribute that overrides Ignorable for specific types inside the ignored namespaces.)
Expression Blend takes advantage of this feature to do things like add design-time properties to XAML content that can be ignored at runtime.
mc:Ignorable can be given a space-delimited list of namespaces, and mc:ProcessContent can be given a space-delimited list of elements. When XamlXmlReader encounters ignorable content that can’t be resolved, it doesn’t report any nodes for it. If the ignorable content can be resolved, it will be reported normally. So consumers don’t need to do anything special to handle markup compatibility correctly.
I've spent the better of half a day trying to make the ItemTemplate of a ListView with a UserControl configurable through means of a DependencyProperty on said UserControl. I've come across some weird inconsistencies regarding the two different Binding methods available on Windows 10 UAP platform ( Binding and x:Bind).
The UserControl looks like this and is part of a custom calendar component.
<UserControl
x:Class="FlowDesigner.UserControls.CalendarDayView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:FlowDesigner.UserControls"
xmlns:vw="using:FlowDesigner.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:uc="using:FlowDesigner.UserControls"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400"
x:Name="DateControl">
<UserControl.Resources>
<DataTemplate x:Key="DefaultDataTemplate" x:DataType="vw:Event" >
<uc:EventListTemplate IsToday="{Binding Date, Converter={StaticResource IsTodayConverter}}"
Date="{Binding Date, Mode=OneWay}"
Summary="{Binding Path=Summary, Mode=OneWay}" />
</DataTemplate>
</UserControl.Resources>
<RelativePanel Background="White" BorderBrush="Black" BorderThickness="1" DataContext="{Binding ElementName=DateControl}">
<TextBlock x:Name="DayText" TextAlignment="Center" VerticalAlignment="Center" />
<TextBlock x:Name="MonthText" TextAlignment="Center" VerticalAlignment="Center" RelativePanel.RightOf="DayText" />
<ListView x:Name="EventList" ItemsSource="{x:Bind Events, Mode=OneWay}"
ItemTemplate="{Binding Path=EventItemTemplate, Mode=OneWay, FallbackValue={StaticResource DefaultDataTemplate}, TargetNullValue={StaticResource DefaultDataTemplate}}"
RelativePanel.Below="DayText" RelativePanel.AlignLeftWithPanel="True" RelativePanel.AlignRightWithPanel="True">
</ListView>
</RelativePanel>
</UserControl>
The EventItemTemplate is a DependencyProperty of the UserControl.
public DataTemplate EventItemTemplate
{
get { return (DataTemplate)GetValue(EventItemTemplateProperty); }
set { SetValue(EventItemTemplateProperty, value); }
}
public static readonly DependencyProperty EventItemTemplateProperty =
DependencyProperty.Register("EventItemTemplate", typeof(DataTemplate), typeof(CalendarDayView), new PropertyMetadata(null));
Which is altered on one of the root pages to style the ListView in one way or the other, like so.
<Style TargetType="uc:CalendarDayView">
<Setter Property="EventItemTemplate">
<Setter.Value>
<DataTemplate x:DataType="vw:Event" >
<TextBlock Text="{Binding Summary, Mode=OneWay}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
This is actually a working version, but I had to tinker around with it quite a bit. The first attempts were made by me with both x:Bind and Binding and without the DataContext on the RelativePanel as the UserControl is now. x:Bind would be functional when setting a value to EventItemTemplate in the root page, but it would not be able to use the default DataTemplate specified by the StaticResource when the root page did not specify anything. Binding on the other hand would use the default DataTemplate at all times, even when the the root page had a set an other value to EventItemTemplate.
By setting the DataContext on the RelativePanel to the UserControl Binding started worked like wanted it too. x:Bind still shows the same behavior.
Now I understand that Binding does not by default bind to the UserControl's DataContext, but I'm still not entirely sure why x:Bind doesn't work. Is this intended behavior or is there something wrong with my entire scheme here and is what I came up with just a lucky hack?
From {x:Bind} markup extension:
The {x:Bind} markup extension—new for Windows 10—is an alternative to {Binding}. {x:Bind} lacks some of the features of {Binding}, but it runs in less time and less memory than {Binding} and supports better debugging.
At XAML load time, {x:Bind} is converted into what you can think of as a binding object, and this object gets a value from a property on a data source. The binding object can optionally be configured to observe changes in the value of the data source property and refresh itself based on those changes. It can also optionally be configured to push changes in its own value back to the source property. The binding objects created by {x:Bind} and {Binding} are largely functionally equivalent. But {x:Bind} executes special-purpose code, which it generates at compile-time, and {Binding} uses general-purpose runtime object inspection. Consequently, {x:Bind} bindings (often referred-to as compiled bindings) have great performance, provide compile-time validation of your binding expressions, and support debugging by enabling you to set breakpoints in the code files that are generated as the partial class for your page. These files can be found in your obj folder, with names like (for C#) .g.cs.
First of all let me say that I'm rather new to WPF, so excuse me for any silly mistakes, but I've been cracking my head at this for a while now.
I have a simple sollution with three classes:
public class MyCustomItem
public class MyCustomLayout : ThirdPartyLayout<MyCustomItem>
public class MyViewController : INotifyPropertyChanged
MyCustomItem is a simple class with some properties ("Name" being one of them). ThirdPartyLayoutTool is a generic component inside an SDK that inherits from System.Windows.Controls.Panel. And MyViewController is the view controller I'm using as a data content.
I then created this simple XAML as the projects main window:
<Window x:Class="DependencyViewer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sdk="clr-namespace:Sdk;assembly=Sdk"
xmlns:local="clr-namespace:MyNamespace"
Title="MainWindow" Height="350" Width="525">
<local:MyCustomLayout x:Name="myLayout"/>
</Window>
Everything displays accordingly.Now my objective is to enhance the display of one of the sub components that is displayed by the ThirdPartyLayout panel, called TargetControl. So I add the following code:
<Window.Resources>
<Style TargetType="{x:Type sdk:TargetControl}">
<Style.Resources>
<ToolTip x:Key="ToolTipContent">
<StackPanel>
<TextBlock FontWeight="Bold" Text="Testing 1 2 3"/>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</ToolTip>
</Style.Resources>
<Setter Property="ToolTip" Value="{StaticResource ToolTipContent}"/>
</Style>
</Window.Resources>
When I run the code, the "Testing 1 2 3" message appears correctly, however, I don't see the Name property. On the output window, I get the following message:
BindingExpression path error: 'Name' property not found on 'object' ''MyViewController' (HashCode=31884011)'
What I don't get is why the binding is happening on the MyViewController class, instead of TargetControl class. Any ideas?
Best regards,
Carlos Jourdan
EDIT:
After tinkering guide mainly by the recommendations given by newb, I eventually found out that the source of the error is in fact in the SDK. The current release is still faulty, but when compiling from source I get the expected behavior.
Thanks for the help.
When you create a binding in XAML, you are, by default, binding to the current DataContext. In this isntance, it seems that MyViewController is the DataContext of the sdk:TargetControl. To bind to the Name property of the skd:TargetControl instead, try the following:
<TextBlock Text="{Binding Name, RelativeSource={RelativeSource AncestorType={x:Type sdk:TargetControl}}}"/>
Seems like DataContext of xaml.cs of MyViewController has the reference of MyCustomItem.
If you want you can set in xaml.cs, MyCustomLayout.ItemsSource = this.DataContext.
Or you can do MyCustomLayout.ItemsSource = specific property of MyCustomItem.
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();