WPF binding user control with data in C# code - wpf

I've create user control like this:
public partial class View
{
public View()
{
InitializeComponent();
}
public static DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(TeaserView) );
public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
}
XAML:
<UserControl x:Class="Controls.View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="200" Width="164">
<Grid VerticalAlignment="Stretch"
x:Name="Preview">
<Label Height="28" Content="{Binding ElementName=Preview, Path=Name}" Background="LightYellow" x:Name="name" VerticalAlignment="Top" ></Label>
</Grid>
</UserControl>
and use it in Window1 simply in XAML:
<controls:View Height="200" Name="View1" Width="164" />
and I try set the Content in C# (Name property in this sample) but it does'n work, label's content is still empty. (All refereces, etc. are good) What's wrong?

Your code is wrong. You bind to Grid.Name property, which is "Preview", not to View.Name.
I really encourage you to go read from A to Z "DataBinding Overview" on MSDN. It worth your time, trust me :). In fact whole "Windows Presentation Foundation" section would be worth your attention.
As for your code, the following will work:
<UserControl x:Class="WpfApplication5.View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300"
Width="300"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Label Height="28"
Content="{Binding Path=Name}"
Background="LightYellow"
VerticalAlignment="Top"/>
</Grid>
</UserControl>
But are you sure you want to hide "Name" property from parents?

Have you set the datacontext on the user control? Try setting it to point to its own codebehind:
DataContext="{Binding RelativeSource={RelativeSource Self}}"

I've put the Name property just as sample. I'm trying to set Label Content in Window1.xaml.cs like:
View1.Name = "Casablanca";

Try the following binding, it should work:
<Label Content="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:View}}, Path=Name}" />
You should also define a xmlns:local="whatever_path_you_have" on the top of the file.
I also suggest renaming "Name" DP to something else to avoid name collusion.

Copied your exact code and it works fine.
However, it's not doing what you're probably expecting it to do. You're setting the source of the binding to the Grid instance. Therefore, the Name property will yield "Preview". The Name property you've defined in your UserControl is ignored because there's already a Name property on UserControl.

Related

How can I turn off default validation in WPF?

It seems that WPF has some validation rule which is turned on by default. When I enter non numeric text into the bound textbox and tab out it, a read border shows around it. What is going on here? I Have set ValidatesOnExceptions to false, where is the validation rule coming from? I am using version 4.5.2 of the .Net framework.
Here is my XAML
<Window x:Class="WpfApplication2.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:WpfApplication2"
mc:Ignorable="d"
Title="MainWindow" Height="159.206" Width="193.953">
<Grid>
<TextBox x:Name="textBox" Height="23" HorizontalAlignment="Left" VerticalAlignment="Top" TextWrapping="Wrap"
Text="{Binding Foo, ValidatesOnExceptions=False, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
Width="91" Margin="10,10,0,0"/>
<TextBox x:Name="textBox1" HorizontalAlignment="Left" Height="23" Margin="10,48,0,0"
TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="91"/>
</Grid>
</Window>
Here is the code behind
namespace WpfApplication2
{
public partial class MainWindow : Window
{
public int Foo { get; set; } = 42;
public MainWindow()
{
InitializeComponent();
}
}
}
You can never set an int property to anything else than a valid int value.
This "validation", or rather type-safety feature of the programming language, cannot be turned off.
You can however get rid of or customize the the default error template if you want to. Just set the Validation.ErrorTemplate property to an emoty ControlTemplate:
<TextBox x:Name="textBox" Height="23" HorizontalAlignment="Left" VerticalAlignment="Top" TextWrapping="Wrap"
Text="{Binding Foo, ValidatesOnExceptions=False, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
Width="91" Margin="10,10,0,0">
<Validation.ErrorTemplate>
<ControlTemplate />
</Validation.ErrorTemplate>
</TextBox>
On app startup, you can add this call to FrameworkCompatibiltyPreferences to disable all textboxes from messing with your input:
class App
{
[STAThread]
public static void Main(params string[] args)
{    
System.Windows.FrameworkCompatibilityPreferences.KeepTextBoxDisplaySynchronizedWithTextProperty = false;
Application app = new Application();
app.Run(new Window1());
}
}
If you're using data binding, it appears that the textboxes will still protect against invalid input. A validation error will be shown (default being the red border) and the view model property will not be set with the invalid value. But now you can input whatever string you want. Makes entering decimal values bound to float or double properties MUCH easier.

Binding disappears on simple user control

I have a simple usercontrol with a single DependencyProperty. I am unable to set bindings on this property. I don't get any exceptions but the bindings just disappear.
I cannot begin to see what is going wrong here. It's so simple.
Here's my usercontrol:
XAML:
<UserControl x:Class="Test.Controls.SimpleUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="ucThis">
<TextBox Text="{Binding Path=MyString, ElementName=ucThis}" />
</UserControl>
Code:
public partial class SimpleUserControl : UserControl
{
public SimpleUserControl()
{
InitializeComponent();
}
public string MyString
{
get { return (string)GetValue(MyStringProperty); }
set { SetValue(MyStringProperty, value); }
}
public static readonly DependencyProperty MyStringProperty =
DependencyProperty.Register("MyString", typeof(string),
typeof(SimpleUserControl), new UIPropertyMetadata("simple user control"));
}
XAML from a test app:
<StackPanel>
<testControls:SimpleUserControl MyString="{Binding Path=TestString}"
x:Name="simpleUC" />
<Label Content="From control" />
<Border Margin="5"
BorderBrush="Black"
BorderThickness="1"
Visibility="{Binding Path=MyString, ElementName=simpleUC, Converter={StaticResource nullVisConv}}">
<ContentPresenter Content="{Binding Path=MyString, ElementName=simpleUC}" />
</Border>
<TextBlock Text="Value from control is null."
Margin="5"
Visibility="{Binding Path=MyString, ElementName=simpleUC, Converter={StaticResource nullVisConv}, ConverterParameter={custom:BooleanValue Value=True}}" />
<Label Content="From binding" />
<Border Margin="5"
BorderBrush="Black"
BorderThickness="1"
Visibility="{Binding Path=TestString, Converter={StaticResource nullVisConv}}">
<ContentPresenter Content="{Binding Path=TestString}" />
</Border>
<TextBlock Text="Value from binding is null."
Margin="5"
Visibility="{Binding Path=TestString, Converter={StaticResource nullVisConv}, ConverterParameter={custom:BooleanValue Value=True}}" />
<TextBox Text="You can set focus here." />
</StackPanel>
The main window for the test app has a property named TestString, is its own DataContext and implements INotifyPropertyChanged correctly. SimpleUserControl.MyString updates as it should but the property it is bound to (TestString) does not. I have inspected this with Snoop; the binding I set on the SimplerUserControl is just not present at run time. What is happening here?
UPDATE
Okay. So if I specify Mode=TwoWay the binding works. That's great. Can anyone explain to me why it behaves this way?
Thanks.
Working as designed :). DPs default to 1-way. Personally, I would change your DependencyProperty.Register() call to make the DP default to two-way. That way you don't have to specify two-way explicitly every time you use it. You'll notice that the framework typically makes DPs two-way by default when you'd want the property to write back. Just a convienience.
Yes you need to provide TwoWay Mode for Dependency Property:
public string MyString
{
get { return (string)GetValue(MyStringProperty); }
set { SetValue(MyStringProperty, value); }
}
public static readonly DependencyProperty MyStringProperty =
DependencyProperty.Register("MyString", typeof(string),
typeof(UserControl1), new FrameworkPropertyMetadata("simple user control", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

WPF-MVVM Binding ViewModel-Property to nested UserControl

As the title says, I want to bind a property from my ViewModel to a nested UserControl in the corresponding view.
I cant get it work the way I need.
The nested UserControl is nothing more than a DatePicker and a DropDown for the hours. How can I tell the DatePicker to choose the date propagated by the ViewModel as its selected date?
I tried nearly everything and now I'm not far away from jumping outside the window.
As you can see any help is appreciated ;)
Now to the code so far: DateTimePicker.xaml.cs (CodeBehind)
public partial class DateTimePicker
{
public static DependencyProperty SelectedDateValueProperty = DependencyProperty.Register("SelectedDateValue", typeof (DateTime), typeof (DateTimePicker), new FrameworkPropertyMetadata(default(DateTime), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnPropertyChangedCallback));
private static void OnPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
Debug.WriteLine("Wohoo. I'm here and still debugging...");
}
public DateTimePicker()
{
InitializeComponent();
DataContext = this;
var times = GetTimes();
Times.ItemsSource = times;
Times.SelectedItem = times.First();
}
public DateTime SelectedDateValue
{
get { return (DateTime) GetValue(SelectedDateValueProperty); }
set { SetValue(SelectedDateValueProperty, value); }
}
}
The nested UserControl (DateTimePicker.xaml):
<UserControl x:Class="Framework.Controls.DateTimePicker"
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="30" d:DesignWidth="200"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<DatePicker HorizontalAlignment="Left" Name="DatePickerCalendar" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Center" SelectedDate="{Binding SelectedDateValue}" />
<ComboBox Grid.Column="1" VerticalAlignment="Center" Name="Times" DisplayMemberPath="Name" />
</Grid>
And, last but not least: The View which has the nested UserControl (View.xaml)
<CustomControls:DateTimePicker SelectedDateValue="{Binding LocalRegistrationStartDate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
Hope the problem is clear and anybody can help me or get the point at what i am doing wrong here.
Using:
"{Binding SelectedDateValue}"
tells WPF "Hey check my DataContext for a property called SelectedDateValue".
What you want is, to get the Property from your user control.
The easiest way is to give your user control a name like:
<UserControl x:Name="myControl"/>
and then modify your binding to :
"{Binding ElementName=myControl, Path=SelectedDateValue}"
The usual way WPF controls are implemented is to use a template rather than defining the control as direct content, like you're doing here. By using a Template, you have access to TemplateBinding, allowing you to easily bind your control properties. See the Control Customization MSDN page.
<ControlTemplate TargetType="{x:Type local:DateTimePicker>
...
<DatePicker SelectedDate="{TemplateBinding SelectedDateValue}" />
...
</ControlTemplate>

Is it possible to bind code-behind property without setting DataContext?

As titled,
I seen couples of similiar question this or this in SO, but I don't see a solution for it.
I know if I need to bind to the code-beind, I need to set Datacontext = this
But my problem is that my datacontext already binding to my ViewModel, but I want to do some UI manipulation with using Command which is defined in the code-beind.
Is it possbile to bind it in xaml? If so, how?
EDIT: I did tried the follows:
<Window x:Class="WpfApplication3.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300" x:Name="_Root">
<Grid x:Name="hellogrid">
<TextBlock x:Name="myTextBlock" Text="AAAA"/>
<Button Margin="82,119,121,120" Name="button2" Content="{Binding Path=Text, ElementName=myTextBlock}"/>
<Button Margin="82,72,121,0" Name="button3" Content="{Binding Path=MyText, ElementName=_Root}" Height="23" VerticalAlignment="Top" />
</Grid>
And code-behind:
public partial class Window1 : Window
{
public string MyText { get; set; }
public Window1()
{
InitializeComponent();
MyText = "ABC";
}
}
I could see the Button2 shows AAAA, but Button3 shows nothing....
Of course
There are many types of bindings. The most basic one binds to a property on the DataContext, which is usually inherited from a Parent object
<DataTemplate DataType="{x:Type MyModel}">
<!-- DataContext is object of type MyModel -->
<local:MyView />
</DataTemplate>
Or
<Window x:Name="MyWindow">
<!-- DataContext Inherited from Window -->
<TextBlock Text="{Binding SomeProperty}" />
</Window>
where
var SomeObject = new SomeModel();
SomeObject.SomeProperty = "Test";
myWindow.DataContext = SomeObject;
Other binding types include ElementName, where you can specify the target UI element to use as the data source for the binding
<StackPanel>
<CheckBox x:Name="SomeCheckBox" />
<TextBlock Text="{Binding ElementName=SomeCheckBox, Path=IsChecked}" />
</StackPanel>
or
<local:MyUserControl x:Name="SomeUserControl">
<Button Command="{Binding ElementName=SomeUserControl, Path=DataContext.SaveCommand}" />
</local:MyUserControl >
Or RelativeSource, which allows you to find an object relative to the current object to use as a DataSource
<Window Title="Test">
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=Title}" />
</Window>
or
<local:MyUserControl>
<Button Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:MyUserControl}}, Path=DataContext.SaveCommand}" />
</local:MyUserControl >
And TemplateBinding, which binds is a shortcut to a RelativeSource binding that binds to a templated object
<Button Content="Test">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<TextBlock Text="{TemplateBinding Content}" />
</ControlTemplate>
</Button.Template>
</Button>
EDIT
The best solution IMO is the one posted by #Saad Imran in this SO question...
With this solution all you have to do is name your window and binding to a property in your XAML will be as easy as this {Binding ElementName=MyWindowName, Path=MyText}
So, what you are doing with Content="{Binding Path=MyText, ElementName=_Root}" is exactly right and your Button Content property IS bound to MyText property but the only thing you are missing is change notification (need to implement INotifyPropertyChanged interface for that) so when you set your MyText property to ABC MyText = "ABC"; no change notification is sent...
Easy way to test this is by setting the MyText property explicitly as such:
private string myText = "ABC";
public string MyText
{
get { return myText; }
set { myText = value; }
}
or setting it in the constructor before InitializeComponent() is called:
MyText = "ABC";
InitializeComponent();
If you do that you'll notice that your button will have ABC as its content but changes to MyText property will not affect the button content because there is no change notification...
Sure, you can use ElementName:
<Window Name="root"
Class="..."
...>
...
<TextBox Text="{Binding Path=Foo, ElementName=root}" />
You could also do it with RelativeSource, but the syntax is uglier...

wpf trouble using dependency properties in a UserControl

I made a UserControl that is meant to be updated once every few seconds with data from a serial port. This UserControl should be very simple, consisting of a Label for a field name, and another Label containing the field value. I say that it should be simple, but it doesn't work. It does not update at all, and doesn't even display the field name.
Below is the code:
public partial class LabeledField : UserControl {
public LabeledField() {
InitializeComponent();
}
public string fieldName {
get { return fieldNameLabel.Content.ToString(); }
set { fieldNameLabel.Content = value; }
}
public string fieldValue {
get { return (string)GetValue(fieldValueProperty); }
set { SetValue(fieldValueProperty, value); }
}
public static readonly DependencyProperty fieldValueProperty =
DependencyProperty.Register(
"fieldValue",
typeof(string),
typeof(LabeledField),
new FrameworkPropertyMetadata(
"No Data"
)
)
;
}
Here is the XAML:
<UserControl x:Class="DAS1.LabeledField" Name="LF"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Horizontal">
<Label Width="100" Height="30" Background="Gray" Name="fieldNameLabel" />
<Label Width="100" Height="30" Background="Silver" Name="fieldValueLabel" Content="{Binding fieldValue}" />
</StackPanel>
And here is the XAML for the Window which references the UserControl. First the header:
<Window x:Class="DAS1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:me="clr-namespace:DAS1"
Title="Window1" Height="580" Width="780">
Then the UserControl itself:
<me:LabeledField fieldName="Test" Width="200" Height="30" fieldValue="{Binding businessObjectField}"/>
If I knew of a more specific question to ask, I would--but can anyone tell me why this doesn't work?
Turns out that in the XAML for the user control, the binding was incorrectly specified.
Originally it was:
<Label Width="100" Height="30" Name="fieldValueLabel" Content="{Binding fieldValue}" />
But I had not specified the element to which fieldValue belongs. It should be (assuming my user control is named "LF":
<Label Width="100" Height="30" Name="fieldValueLabel" Content="{Binding ElementName=LF, Path=fieldValue}" />
If you want to bind to properties of the control, you should specify so in the binding. Bindings are evaluated relative to DataContext if their source isn't explicitly specified, so your binding doesn't bind to your control, but to the inherited context (which is likely missing the property you're binding to). What you need is:
<Label Width="100" Height="30" Name="fieldValueLabel"
Content="{Binding Path=fieldValue, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DAS1.LabeledField}}}" />
You really don't need a dependency property on your user control, in fact you should strive to keep user controls without anything special in the code-behind, custom Controls should be used for that.
You should define your UserControl like this (without any code behind):
<UserControl x:Class="DAS1.LabeledField"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Horizontal">
<Label Width="100" Height="30" Name="fieldNameLabel" Content="{Binding fieldName}" />
<Label Width="100" Height="30" Name="fieldValueLabel" Content="{Binding field}" />
</StackPanel>
Then, make sure your business object implements INotifyPropertyChanged, because you cannot update from your business object efficiently without modifying it at least this much. The fieldName is just an example how you could bind the display name on the label automatically to a property on your business object.
Then, simply make sure the DataContext of your UserControl is your business object.
How will this work? The Label.Content property is a DependencyProperty and will support binding itself. Your business object implements INotifyPropertyChanged and thus supports updates to binding - without it, the binding system does not get notified when your field's value changes, regardless if you bound it to a DependencyProperty on one end.
And if you want to reuse this user control elsewhere, simply place a desired instance of your business object to the DataContext of the desired LabeledField control. The binding will hook up itself from the DataContext.

Resources