I have a window that get its data from another class that is passed as DataContext. But I now also want to do data binding within the window. The window looks as follows:
<Window x:Class="WpfApplication1.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">
<StackPanel>
<TextBlock Text="{Binding UserName}" />
<TextBlock x:Name="TestTextBlock"
Text="Hello World" />
<TextBlock x:Name="TestTextBlock2"
Text="{Binding ElementName=TestTextBlock,Path=Text}" />
</StackPanel>
</Window>
The binding between the text blocks TestTextBlock and TestTextBlock2 works fine, but only until I change the DataContext-property of the window. How can I bind between those two textblocks so that changing the DataContext will not break the data binding?
Thanks in advance,
Stefan
Try setting the Binding.Mode to OneTime explicitly.
That way, TestTextBlock2.Text will only be set once (if that's what you want).
Related
I'm not sure why it seems that every time I work with WPF, things are always much more difficult that WinForms or C/SDK.
In this case, the window looks like this in the designer.
But here's how it looks at run time.
And my XAML:
<Window x:Class="InsiderArticlesManager.AuthorWindow"
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:InsiderArticlesManager"
mc:Ignorable="d"
Title="Set Author" Height="114" Width="341" WindowStartupLocation="CenterScreen" ResizeMode="NoResize" SourceInitialized="Window_SourceInitialized" WindowStyle="SingleBorderWindow">
<StackPanel>
<Label Name="lblPrompt" Content="Select Author:" Margin="5,5,5,0" />
<ComboBox Name="UserList" DisplayMemberPath="Email" Margin="5,0,5,5"></ComboBox>
<WrapPanel HorizontalAlignment="Right">
<Button Name="btnOk" Content="OK" Width="78" Margin="5,5,0,5" IsDefault="True" Click="Ok_Click" />
<Button Name="btnCancel" Content="Cancel" Width="78" Margin="5,5,5,5" IsCancel="True" />
</WrapPanel>
</StackPanel>
</Window>
I thought the whole point of the designer was so that I could see how the window will look. Since it shows me something different, how do I know what size to set it?
You might want to set the Width and Height on the StackPanel and set the Window's SizeToContent to WidthAndHeight. That way the content is always visible, no matter the size of the window border (assuming you size your StackPanel correctly :)):
<Window x:Class="InsiderArticlesManager.AuthorWindow"
...
SizeToContent="WidthAndHeight">
<StackPanel Height="114" Width="341">
...
</StackPanel>
</Window>
In your case the difference between design time and runtime occurs because the XAML designer window has a smaller window border size.
<UserControl x:Class="WatermarkTextBox"
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"
d:DesignHeight="30"
d:DesignWidth="250">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</UserControl.Resources>
<Border>
<Grid x:Name="grid">
<TextBlock Text="{Binding Watermark, FallbackValue=This prompt dissappears as you type...}"
Visibility="{Binding ElementName=txtUserEntry, Path=Text.IsEmpty, Converter={StaticResource BooleanToVisibilityConverter}}" />
<TextBox Name="txtUserEntry"
Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</Border>
</UserControl>
The above code shows my WatermarkTextBox control. In the code behind file I have set the DataContext. I left out all the code for the DP's of the control.
public WatermarkTextBox()
{
InitializeComponent();
grid.DataContext = this;
}
I had to bind the DataContext to the grid because otherwise the Text properties of both the watermark and actual text wouldn't display. The problem now is that I can't set the Background of the Border outside of the Grid.
I tried the code below but then only the Background of the Border is set and not the watermark and actual text.
public WatermarkTextBox()
{
InitializeComponent();
this.DataContext = this;
grid.DataContext = this;
}
In a UserControl like this you should never exlicitly set the DataContext to this or anyting else, because the DataContext is usually set externally when you use the UserControl somewhere in your application. The externally applied DataContext is typically (part of) the application's view model.
You should instead change your internal bindings so that they use an explicit RelativeSource:
<TextBlock
Text="{Binding Path=Watermark,
RelativeSource={RelativeSource AncestorType=UserControl},
FallbackValue=This prompt dissappears as you type...}"
Visibility="{Binding ElementName=txtUserEntry,
Path=Text.IsEmpty,
Converter={StaticResource BooleanToVisibilityConverter}}" />
<TextBox
Name="txtUserEntry"
Text="{Binding Path=Text,
UpdateSourceTrigger=PropertyChanged,
RelativeSource={RelativeSource AncestorType=UserControl}}" />
and then remove any DataContext assignment from the UserControl's constructor.
See e.g. this answer (and many other similar) that discuss this topic in detail.
I have a user control with ContentObject property and I use it to specify custom content. On my main window I created a nested label bound to a dependency property. It works fine if I bind it with RelativeSource, but for some reason it doesn't work if I reference element by name:
MainWindow.xaml:
<Window x:Class="TestContentPresenter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestContentPresenter"
Title="MainWindow" Height="350" Width="525" Name="MyWindow">
<Grid>
<local:MyUserControl>
<local:MyUserControl.ContentObject>
<!--<TextBlock Text="{Binding MyText, ElementName=MyWindow}"/>-->
<TextBlock Text="{Binding MyText, RelativeSource={RelativeSource AncestorType=Window}}"/>
</local:MyUserControl.ContentObject>
</local:MyUserControl>
</Grid>
</Window>
UserControl.xaml:
<UserControl x:Class="TestContentPresenter.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestContentPresenter"
Height="300" Width="300" Name="MainControl">
<StackPanel>
<ContentPresenter Content="{Binding ContentObject, ElementName=MainControl}"/>
</StackPanel>
</UserControl>
Main window has MyText dependency property and the commented line is what doesn't work. I suspect that this has something to do with name scopes, but is there anything I'm doing wrong?
No, you are not really doing anything wrong, it's just that the by the time the binding to the TextBlock's Text property gets resolved there is no element named "MyWindow" in scope. You set the ContentObject property of the UserControl to a TextBlock but when the UserControl eventually renders it doesn't know anything about any "MyWindow" name.
You could solve this by simply replacing the ElementName with a RelativeSource - as you have already discovered - since the TextBlock will always have a parent window. ElementName won't work.
Why doesn't this binding update?
code:
MainWindow.xaml
<Window x:Class="WpfApplication12.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication12"
Height="350" Width="525">
<StackPanel>
<local:UserControl1 x:Name="usr" />
<TextBlock Text="{Binding ElementName=usr, Path=txt.Text}" />
</StackPanel>
</Window>
UserControl1.xaml
<UserControl x:Class="WpfApplication12.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextBox Text="qwe" x:Name="txt" />
</UserControl>
The TextBox inside the UserControl is inaccessible due to its protection level, also it is a field, you can never bind to those. You would need to expose it in the code behind of the UserControl as public property.
public TextBox Txt
{
get { return txt; }
}
Edit: As Henk Holterman pointed out you might not want to expose the whole TextBox, so you could define a dependency property to which the TextBox internally binds for example.
I'm in the process of creating a simple user control; just an ImageButton.
I've already successfully bound the Image to the button and so I've decided to add a tooltip. Now I'm having troubles. It seems that I can hard-code the text for the tooltip in the XAML for the control, but when it's bound it's returning an empty string.
Here's the XAML for my control:
<Button x:Class="BCOCB.DACMS.Controls.ImageButton"
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"
Name="this"
Style="{StaticResource DisabledButton}">
<Image Source="{Binding ElementName=this, Path=Source}" />
<Button.ToolTip>
<TextBlock Text="{Binding ElementName=this, Path=ToolTipText}" />
</Button.ToolTip>
</Button>
And here's the dependency property info for the tooltip text:
public static readonly DependencyProperty ToolTipTextProperty = DependencyProperty.Register("ToolTipText", typeof(string), typeof(ImageButton));
public string ToolTipText
{
get
{
return this.GetValue(ToolTipTextProperty) as string;
}
set
{
this.SetValue(ToolTipTextProperty, value);
}
}
And, finally, the declaration of the control in my Window:
<controls:ImageButton x:Name="btnAdd" Source="/DACMS;component/Resources/plus.png" ToolTipText="Add New Item" Click="btnAdd_Click" />
As I mentioned before, the image binds just fine and I've done it in exactly the same manner.
Any ideas?
Thanks,
Sonny
EDIT: I have it working now. I've removed the ElementName from the binding and set the TextBlock's DataContext = this in the code behind on instanciation. Still, I'd like to know how to fix this in the XAML, instead.
I'm unable to test this right now, but you can try:
<Button.ToolTip
DataContext=”{Binding Path=PlacementTarget.Parent.Parent,
RelativeSource={x:Static RelativeSource.Self}}"
>
<TextBlock Text="{Binding Path=ToolTipText}" />
</Button.ToolTip>
You may have to experiment a little with the number of "Parent" in PlacementTarget.
Hopefully this works. I don't like giving answers that I haven't tested, but I don't have VS on this computer. :)
I've had this same problem with binding to a ContextMenu. After my research I think that it is because the ToolTip and ContextMenu do not exist within the visual tree of your page/window/control. And therefore the DataContext is not inherited and makes binding troublesome.
Here is a Xaml hack I found that worked for me.
Binding to a MenuItem in a WPF Context Menu
The way to set the data context to "this" through xaml looks like this:
<Control DataContext={Binding RelativeSource={RelativeSource Self}}>
As another point, wpf buttons allow their content to be just about any (single) thing you want. If you want something other than text (ie, text and an image), it looks like this:
<Button Name="SampleButton" Click="SampleButton_Click">
<Grid Width="70" Height="62">
<Label Content="SampleText"/>
<Image Margin="3,3,3,3" Source="Graphics/sample.ico"/>
</Grid>
</Button>
Since you aren't changing anything but the Text on the tooltip TextBlock you can just use an inline declaration which will generate the TextBlock for you and doesn't require any hacking to get around the name scoping issue you're running into otherwise:
<Button ... ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=ToolTipText}">...
You could alternately set the ToolTip on the Image and use the control as the DataContext, which gets around the name scoping problem. The DataContext will be passed to the ToolTip, allowing normal binding:
<Image DataContext="{Binding ElementName=this}" Source="{Binding Source}">
<Image.ToolTip>
<TextBlock FontSize="18" Text="{Binding Path=ToolTipText}" />
</Image.ToolTip>
</Image>
This way allows additional settings on the TextBlock or more complex visuals.
This fixes the Problem with the Tooltip Bindings and Dependencies Properties:
<UserControl x:Class="Extended.InputControls.TextBoxUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Extended.InputControls"
x:Name="UserControl"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<TextBox x:Name="textBox">
<TextBox.ToolTip>
<ToolTip Content="{Binding Path=CustomToolTip}" Background="Yellow"/>
</TextBox.ToolTip>
</TextBox>
</UserControl>
Instead of this ( doesnt Work ):
<UserControl x:Class="Extended.InputControls.TextBoxUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Extended.InputControls"
x:Name="UserControl">
<TextBox x:Name="textBox">
<TextBox.ToolTip>
<ToolTip Content="{Binding ElementName=UserControl, Path=CustomToolTip}" Background="Yellow"/>
</TextBox.ToolTip>
</TextBox>
</UserControl>