Binding error on WPF (biding to DataContext) - c#

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.

Related

Binding works fine, but intellisense says: Cannot resolve property XXX in data context of type 'object'

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.

Binding vs. x:Bind, using StaticResource as a default and their differences in DataContext

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.

WPF TextBox with Hint As Custom/User Control

So, I'm trying to design as minimalistic a UI as possible, and to that end, I need to provide hints inside textboxes, like android does. I've found many solutions to the problem (see Watermark / hint text / placeholder TextBox in WPF , How can I add a hint text to WPF textbox?) but every solution seems to use lots of XAML code, styles, triggers and the sort. What I want to do is, I want to have a textbox subclass that has a HintText property which I can use everywhere, but so far, I haven't managed to even get close. This is the closest I got:
<TextBox x:Class="MyProgram.CustomControls.HintTextBox"
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" Text="ASDF"
d:DesignHeight="174" d:DesignWidth="708">
<TextBox.Resources>
<VisualBrush x:Key="VB">
<VisualBrush.Visual>
<Label Content="{Binding Path=HintText}" Foreground="LightGray" FontSize="25"/>
</VisualBrush.Visual>
</VisualBrush>
</TextBox.Resources>
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="Background" Value="{StaticResource VB}"/>
</Style>
</TextBox.Style>
</TextBox>
and:
public partial class HintTextBox : TextBox
{
public HintTextBox()
{
InitializeComponent();
}
public static DependencyProperty HintTextProperty = DependencyProperty.Register("HintText", typeof(String), typeof(HintTextBox));
}
Which is missing the trigger definitions, but that's not the main problem here. My primary problem is that I can't seem to bind the HintText property. I can't assign it through XAML, and I can't bind to it for some reason. I also tried binding to the TextBox's own Text property just to see if it would work, and it didn't. What am I doing wrong? Or am I barking up the wrong tree entirely?
EDIT: I also need the same functionality for a PasswordBox, getting nowhere with that either... Why the hell did they separate TextBox and PasswordBox anyway?
The problem is, that the VisualBrush resource "VB" can be shared between all elements and label content can't be binded.
You can try use my sample TextBox with null text hint

Microsoft UIAutomation fails to retrieve child components in WPF GroupBox

Setting the scene
I have a simple Microsoft WPF application that consists of a (data driven) GroupBox containing a Group of Cats and a group of Dogs. Both the Cat and Dog groups contain two group items each. When I run the application all appears fine, I can see the groups and their content on the screen.
Running the application produces the following window:
However, when I create a UIAutomation test I can’t find any AutomationElements for the group items, just the groups; only the Cat and Dog Groups can be accessed using either the AutomationElement route, or seen in UISpy.exe as per the image below:
The child components for the individual Cat and Dog group items are not present and I need to be able to retrieve them as AutomationElements in my test code:
[TestMethod]
public void MyTest()
{
Condition controlTypeCondition = new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Group);
var foreGroundWindow = GetForegroundWindow();
var collection = foreGroundWindow.FindAll(TreeScope.Descendants, controlTypeCondition);
foreach (AutomationElement element in collection)
{
logger.Debug("Name: " + element.Current.Name);
var children = element.FindAll(TreeScope.Children, Condition.TrueCondition);
logger.Debug("Number of children: " + children.Count);
}
}
The above currently outputs:
Name: Cat
Number of children: 0
Name: Dog
Number of children: 0
Reproducing the Issue
To reproduce this issue, create a new WPF application in Visual Studio (called WpfApplication1) and replace the contents of the MainWindow.xaml with the following:
<Window x:Class="WpfApplication1.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"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
</ResourceDictionary.MergedDictionaries>
<XmlDataProvider x:Key="data">
<x:XData>
<Animals xmlns="">
<Animal name="Felix" Species="Cat" />
<Animal name="Garfield" Species="Cat" />
<Animal name="Rex" Species="Dog" />
<Animal name="Rover" Species="Dog" />
</Animals>
</x:XData>
</XmlDataProvider>
<CollectionViewSource x:Key="AnimalsBySpecies" Source="{Binding Source={StaticResource data}, XPath=Animals/Animal}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="#Species" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</ResourceDictionary>
</Window.Resources>
<ItemsControl ItemsSource="{Binding Source={StaticResource AnimalsBySpecies}}">
<ItemsControl.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<GroupBox Header="{Binding Name}">
<ItemsPresenter />
</GroupBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ItemsControl.GroupStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding XPath=#name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
In reality my code doesn't look exactly like this as it is an MVVM application. But for brevity I have simplified that down into a single XAML file which reproduces the same issue. The key point to note is that the group contents are populated from a XAML Binding.
So how can I get to the content using UIAutomation?
Any help greatly appreciated!
This is a well-known issue where TextBlock's that are inside data templates are not visible in the Control or Content views of the automation tree. This is an optimization made by WPF. There are three possible resolutions.
One is to use .NET 4.5 where, I believe, this was changed so that TextBlock's will now be included.
Another is to make sure you use the Raw view. This is easy in UI Spy (go to View > Raw View) however it somewhat cumbersome in automated tests as you cannot use normal FindFirst or FindAll conditions. You have to use TreeWalker.RawViewWalker to manually inspect the automation tree.
Finally, you can subclass the TextBlock control and override the OnCreateAutomationPeer method to return a custom AutomationPeer implementation with IsControlElement returning true. This outlined in this answer.
Try using the the native-code UI Automation API, instead of the managed API that's part of the .Net framework. This newer API supports additional accessibility, and is recommended by Microsoft. Here's some more information, be sure to read the links in the original post.
In addition, UISpy is deprecated and no longer recommended. Instead, use UIA Verify tool which, in addition to being much more stable and faster than UISpy, also works with the Windows Automation API described above and thus may expose more information about the controls you're interested at.
UISpy is not a good tool to inspect WPF windows or elements. I would suggest using Snoop.
However, this question seems similar and the solution given there may solve your problem.

Binding to Self/'this' in XAML

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.

Categories

Resources