Binding disappears on simple user control - wpf

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));

Related

Bind header and content dynamically to expander

Firstly I see the type for Header and content for the expander, it says the type is object. I have a user control with name CommonExpanderUserControl as follows,
xaml:
<uwpControls:Expander Header="{Binding HeaderContent}" Content="{Binding MainContent}">
</uwpControls:Expander>
In xaml.cs (DataContext is set to this)
public static readonly DependencyProperty HeaderContentProperty =
DependencyProperty.Register("HeaderContent", typeof(object), typeof(CommonExpanderUserControl), new
PropertyMetadata(null));
public object HeaderContent
{
get { return (object)GetValue(HeaderContentProperty); }
set { SetValue(HeaderContentProperty, value); }
}
public static readonly DependencyProperty MainContentProperty =
DependencyProperty.Register("MainContent", typeof(ContentControl), typeof(CommonExpanderUserControl), new
PropertyMetadata(null));
public ContentControl MainContent
{
get { return (ContentControl)GetValue(MainContentProperty); }
set { SetValue(MainContentProperty, value); }
}
Now I am using this UserControl somewhere outside as follows,
<UserControl.Resources>
<ContentControl x:Key="Header">
<Grid x:Name="ExpanderHeaderGrid" HorizontalAlignment="Stretch" Padding="0" Margin="0"
Background="{Binding LisSharedSettings.ChangeHeaderColor,Converter={StaticResource BoolToSolidBrushConverter}}">
<TextBlock x:Name="TextBlockLisSharedSettingsTitle"
x:Uid="/Application.GlobalizationLibrary/Resources/InstrumentSettingsViewLisSettingsTextBlockTitle"
Style="{StaticResource TextBlockStyleSectionHeader}"/>
</Grid>
</ContentControl>
<ContentControl x:Key="Body">
Some content here.
</ContentControl>
</UserControl.Resources>
<Grid>
<local:CommonExpanderUserControl HeaderContent="{StaticResource Header}" MainContent="{StaticResource Body}"/>
</Grid>
Binding Content control like that simply doesn't work. If I remove the MainContent binding and bind only the Header, it says object reference not set to an instance of an object. Please help.
The problem occurs StaticResource binding, we could not bind header with control by StaticResource. And for the scenario the better way is bind HeaderTemplate and send the data source to the header property like the following.
<UserControl.Resources>
<DataTemplate x:Key="HeaderTemplate">
<Grid
x:Name="ExpanderHeaderGrid"
Margin="0"
Padding="0"
HorizontalAlignment="Stretch"
Background="Red"
>
<TextBlock x:Name="TextBlockLisSharedSettingsTitle" Text="{Binding}" />
</Grid>
</DataTemplate>
</UserControl.Resources>
<Grid>
<uwpControls:Expander Header="hello" HeaderTemplate="{StaticResource HeaderTemplate}" />
</Grid>

Binding with DependencyProperty

I've defined a class name TextColumns.cs, which has a DependencyProperty RichTextBlockContentProperty:
public static readonly DependencyProperty RichTextBlockContentProperty =
DependencyProperty.Register("RichTextBlockContent", typeof(string),
typeof(RichTextColumns), new PropertyMetadata(""));
public string RichTextBlockContent
{
get { return (string)GetValue(RichTextBlockContentProperty); }
set //Debug, but the SetValue won't fire
{
SetValue(RichTextBlockContentProperty, value);
}
}
In the XAML, I use it as
<FlipView x:Name="flipView"
ItemsSource="{Binding Source={StaticResource itemsViewSource}}">
<FlipView.ItemTemplate>
<DataTemplate x:Name="myDataTemplate">
<UserControl Loaded="StartLayoutUpdates" Unloaded="StopLayoutUpdates">
<ScrollViewer x:Name="scrollViewer" Style="{StaticResource HorizontalScrollViewerStyle}" Grid.Row="1">
<!-- Content is allowed to flow across as many columns as needed -->
<common:RichTextColumns x:Name="richTextColumns" Margin="117,0,117,47"
RichTextBlockContent="{Binding title}">
<RichTextBlock x:Name="richTextBlock" Width="560" Style="{StaticResource ItemRichTextStyle}">
<Paragraph>
<Run x:Name="RunText" FontSize="26" FontWeight="SemiBold" Text="{Binding title}"/>
</Paragraph>
</RichTextBlock>
</common:RichTextColumns>
</UserControl>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
When the page loaded, it's supposed that the RichTextBlockContent will get the value of the Binding "title", while the Binding in the RichTextBlock worked.
Is there something I've missed?
The setter won't get called. If you need to do logic when the value gets set you need to supply a PropertyChanged callback in the PropertyMetadata Constructor
http://msdn.microsoft.com/en-us/library/ms557330.aspx

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...

Silverlight 4 MVVM: Unable to Databind ICommand

I have created a Silverlight User Control. The markup is:
<StackPanel Grid.Row="4" Grid.Column="0" Orientation="Horizontal" Width="Auto" Margin="5">
<Button Content="OK" Margin="0,0,5,5" MinWidth="50" Command="{Binding OKCommand}" />
</StackPanel>
The code behind declares a Dependency property 'OKCommand' as:
public ICommand OKCommand
{
get
{
return (ICommand)GetValue(OKCommandProperty);
}
set
{
SetValue(OKCommandProperty, value);
}
}
public static readonly DependencyProperty OKCommandProperty
= DependencyProperty.Register("OKCommand", typeof(ICommand), typeof(TestUserControl), new PropertyMetadata(null, OKCommandProperty_PropertyChangedCallback));
private static void OKCommandProperty_PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
Now I want to use the user control on another page where which is the View & the ViewModel defines the command to which I want the OKCommand to be bound. The XAML markup is as such:
<local:TestControl OKCommand="{Binding Path=TestControlOk}"/>
However when I click the button it does not execute anything. Any clues as to what I am doing wrong here.
You need to show the view model that contains the TestControlOk property so we can tell if that's part of the problem.
UserControls do not register themselves as the data context automatically so the binding inside the user control won't have anything to bind to. Do you have
this.DataContext = this;
anywhere in the UserControl codebehind to enable your first binding to actually work?
Alternatively, you can do something like so:
<UserControl .....
x:Name="MyUserControl">
<StackPanel Grid.Row="4" Grid.Column="0" Orientation="Horizontal" Width="Auto" Margin="5">
<Button Content="OK" Margin="0,0,5,5" MinWidth="50"
Command="{Binding OKCommand, ElementName=MyUserControl}" />
</StackPanel>
</UserControl>
Note the ElementName= part of the binding pointing to the root UserControl element in your XAML.

WPF binding user control with data in C# code

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.

Resources