Binding to Static class property and StringFormat - c#

I am able to bind a static class property to a MenuItem header, but I cannot determine how to include a StringFormat so that I can display hard-coded text in addition to the property.
Is this possible?
Currently: (Displays "SQLSERVER1")
Header="{x:Static settings:Settings.CurrentServer}"
Desired: (Display "Connection: SQLSERVER1")
Header="{Binding Source={x:Static Settings:Settings.CurrentServer},StringFormat='Connection: {0}'}"
When I try the 'Desired' line in the XAML the StringFormat is ignored entirely. What am I doing wrong?

The following works for me as of .NET 5:
<Window Title="{Binding Source={x:Static vm:ApplicationSettings.ProductName}, StringFormat='{}{0} Document'}" />
where ProductName is defined as:
public static string ProductName {get { ... ; } }

MenuItem provides a HeaderStringFormat property that you should use:
<MenuItem Header="{Binding Source={x:Static Settings:Settings.CurrentServer}}"
HeaderStringFormat="Connection: {0}" />
Actually, that property is part of HeaderedItemsControl, which MenuItem happens to extend.
The StringFormat property is just ignored.

I suffered a similar problem and got around it by utilising ContentControl and it's separate ContentStringFormat property:
<TextBlock Cursor="Help" Text="Hello World" >
<TextBlock.ToolTip>
<ContentControl Content="{Binding Path=SomeProperty, Source={x:Static local:SomeStaticClass}}" ContentStringFormat="Hello {0}" />
</TextBlock.ToolTip>
</TextBlock>

StringFormat is disregarded for Content and Header, because their TargetType is not System.String. Those properties have corresponding *StringFormat properties to get around that limitation.

Related

WPF tooltip Binding getting override when I set the tooltip from code

I have a User control and I bind the tooltip of that control into some object's property
<usercontrols:ucButton x:Name="xSaveCurrentBtn" ButtonType="ImageButton" ButtonFontImageSize="16" ButtonImageWidth="18" ButtonImageHeight="18" ButtonImageType="Save" Click="xSaveSelectedButton_Click" ButtonStyle="{StaticResource $ImageButtonStyle_Menu}" DockPanel.Dock="Right" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,0,0">
<usercontrols:ucButton.ToolTip>
<ToolTip Content="{Binding ItemName, Mode=OneWay}" ContentStringFormat="Save {0}"/>
</usercontrols:ucButton.ToolTip>
</usercontrols:ucButton>
from the code I set the data context of the ucButton to be my object:
xSaveCurrentBtn.DataContext = WorkSpace.Instance.CurrentSelectedItem;
sometimes the CurrentSelectedItem is null, and if this is the case I want the tooltip to display "No Item Selected"
I tried doing this:
xSaveCurrentBtn.Tooltip = "No Item Selected";
but when the CurrentSelectedItem isn't null and I reset the xSaveBtn.DataContext to that object, I am still seeing the No Item Selected tooltip as if my WPF tooltip section was overriden and its no longer binding into the datacontext ItemName Property
You are trying to set a property to two values at the same time. It's impossible.
What you are doing in XAML is equivalent to:
xSaveCurrentBtn.Tooltip = new ToolTip() {.....};
When you setting a string value to the same property, the previous value is lost. And it is not possible to restore it if you do not save it first.
You might want to assign a value in case of a binding error:
<ToolTip Content="{Binding ItemName,
Mode=OneWay,
FallbackValue='No Item Selected'}"
ContentStringFormat="Save {0}"/>
how can I bind the data context to update to be the new CurrentSelectedItem without explicitly setting it?
Assuming that «WorkSpace.Instance» is an immutable property that returns an instance of «WorkSpace» and «CurrentSelectedItem» is a property with an «INotifyPropertyChanged.PropertyChanged» notification, then you can do this:
<usercontrols:ucButton DataContext="{Binding CurrentSelectedItem, Source={x:Static viewmodels:WorkSpace.Instance}}" ...>
The «viewmodels» prefix depends on the assembly and namespace in which the «WorkSpace» class is declared.
you can use ContentTemplate with TextBlock, which will either use StringFormat, or TargetNullValue depending on ItemName being null:
<usercontrols:ucButton.ToolTip>
<ToolTip Content="{Binding ItemName}">
<ToolTip.ContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding StringFormat='Save {0}',
TargetNullValue='No Item Selected'}"/>
</DataTemplate>
</ToolTip.ContentTemplate>
</ToolTip>
</usercontrols:ucButton.ToolTip>
or if you bind Tooltip.Content differently:
<usercontrols:ucButton.ToolTip>
<ToolTip Content="{Binding}">
<ToolTip.ContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=ItemName,
StringFormat='Save {0}',
FallbackValue='No Item Selected'}"/>
</DataTemplate>
</ToolTip.ContentTemplate>
</ToolTip>
</usercontrols:ucButton.ToolTip>

How do I pass an input via binding to a custom property in UserControl? [duplicate]

I have a UserControl that I want to participate in data binding. I've set up the dependency properties in the user control, but can't get it work.
The uc displays the correct text when I call it with static text (e.g BlueText="ABC") . When i try to bind it to a local public property, it is always blank.
<src:BlueTextBox BlueText="Feeling blue" /> <!--OK-->
<src:BlueTextBox BlueText="{Binding Path=MyString}" /> <!--UserControl always BLANK!-->
<TextBox Text="{Binding Path=MyString}" Width="100"/> <!--Simple TextBox Binds OK-->
I've boiled the code down to the following simplified example. Here is the XAML of the UserControl:
<UserControl x:Class="Binding2.BlueTextBox" ...
<Grid>
<TextBox x:Name="myTextBox" Text="{Binding BlueText}" Foreground="Blue" Width="100" Height="26" />
</Grid>
Here is the code behind of the UserControl:
public partial class BlueTextBox : UserControl
{
public BlueTextBox()
{
InitializeComponent();
DataContext = this; // shouldn't do this - see solution
}
public static readonly DependencyProperty BlueTextProperty =
DependencyProperty.Register("BlueText", typeof(string), typeof(BlueTextBox));
public string BlueText
{
get { return GetValue(BlueTextProperty).ToString(); }
set { SetValue( BlueTextProperty, value.ToString() ); }
}
This seems like it should be really easy, but I can't make it work. Thanks for your help!
More info: When i was trying the fix suggested by Eugene, I noticed some peculiar behavior. I added a PropertyChangedCallback to the metadata; this allows me to watch the value of BlueText getting set. When setting the string to a static value (="feeling blue") the PropertyChanged event fires. The data binding case does not fire PropertyChanged. I think this means the data-bound value is not getting sent to the UserControl. (I think the constructor does not get called in the static case)
Solution: The problems were correctly identified by Arcturus and jpsstavares. First, I was overwriting the data binding when is set DataContext=this in the constructor of the control. This prevented the data bound value from getting set. I also had to name the control x:Name=root, and specify the Binding ElementName=root int the XAML. To get the TwoWay binding, I needed to set Mode=TwoWay in the caller. Here is the correct code:
<src:BlueTextBox BlueText="{Binding Path=MyString, Mode=TwoWay}}" /> <!--OK-->
Now the XAML in the UserControl:
<UserControl x:Class="Binding2.BlueTextBox" x:Name="root"...
<Grid>
<TextBox x:Name="myTextBox" Text="{Binding ElementName=root, Path=BlueText}" Foreground="Blue" Width="100" Height="26" />
</Grid>
Finally I removed the DataContext=this in the constructor of the UserControl.
public BlueTextBox()
{
InitializeComponent();
//DataContext = this; -- don't do this
}
Thanks everyone for the tremendous help!
You set the DataContext in the Control to itself, thus overwriting the DataContext when using this Control in other controls. Taking your binding as example in your situation:
<src:BlueTextBox BlueText="{Binding Path=MyString}" />
Once loaded and all the Datacontext is set, it will look for the path MyString in your BlueTextBox thing control due to you setting the DataContext to it. I guess this is not how you intended this to work ;).
Solution:
Change the text binding either one of the 2 bindings:
{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type src:BlueTextBox}}, Path=BlueText}
or
Name your control Root (or something like that)
<UserControl x:Name="Root"
{Binding ElementName=Root, Path=BlueText}
And remove the
DataContext = this;
from the constructor of your UserControl and it should work like a charm..
I think in this case you need to set the ElementName property in the binding. Something like this:
<UserControl x:Class="Binding2.BlueTextBox" x:Name="blueTextBox"...
<Grid>
<TextBox x:Name="myTextBox" Text="{Binding ElementName=blueTextBox, Path=BlueText}" Foreground="Blue" Width="100" Height="26" />
</Grid>
Possibly you need to add to your property FrameworkPropertyMetadata where specify FrameworkPropertyMetadataOptions.AffectsRender and AffectsMeasure.
FrameworkPropertyMetadataOptions enumeration MSDN article
I know this is an old topic but still.
Also mention the PropertyChangedCallback on the UIPropertyMetadata during registering your DP

WP7: Conditionally show & hide control elements in Data Templates

I have a note application with a ListBox control, which lists all the available notes from the ObservableCollection<Note> Notes. class Note has attributes like
String Title;
bool Has_Reminder;
DateTime Reminder_Date;
What I want is that the TextBlock element, which shows the Reminder_Date, is only shown, if Has_Reminder is true. But I do not know how to access this attribute from my custom control NoteListItem. Its this.DataContext attribute is null, but the control still properly displays the Note's bound attributes handed down by the ListBox ItemsSource. How can I achieve that?
Thanks for your help.
I tried to read the attributes in the constructor, which did not work:
public NoteListItem()
{
InitializeComponent();
Note this_note = LayoutRoot.DataContext as Note; // turns out, this_note is null
if (!this_note.Has_Reminder)
Reminder_Info.Visibility = System.Windows.Visibility.Collapsed;
}
NoteListItem control
<Grid x:Name="LayoutRoot" >
<TextBlock x:Name="Title" Text="{Binding Title}" />
<TextBlock x:Name="Reminder_Date" Text="{Binding Reminder_Date}" />
</Grid>
NoteList control:
<ListBox x:Name="NoteListBox" ItemsSource="{Binding Notes}" >
<ListBox.ItemTemplate>
<DataTemplate>
<local:NoteListItem />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Do you know how to use a converter? Your converter would convert a bool to a Visibility, then you can bind the TextBlock's Visibility to Has_Reminder:
<TextBlock x:Name="Reminder_Date" Text="{Binding Reminder_Date}" Visibility="{Binding Has_Reminder, Converter={...}}"/>
This might help: http://www.jeff.wilcox.name/2008/07/visibility-type-converter/

Data binding to a UserControl in WPF

I have a UserControl that I want to participate in data binding. I've set up the dependency properties in the user control, but can't get it work.
The uc displays the correct text when I call it with static text (e.g BlueText="ABC") . When i try to bind it to a local public property, it is always blank.
<src:BlueTextBox BlueText="Feeling blue" /> <!--OK-->
<src:BlueTextBox BlueText="{Binding Path=MyString}" /> <!--UserControl always BLANK!-->
<TextBox Text="{Binding Path=MyString}" Width="100"/> <!--Simple TextBox Binds OK-->
I've boiled the code down to the following simplified example. Here is the XAML of the UserControl:
<UserControl x:Class="Binding2.BlueTextBox" ...
<Grid>
<TextBox x:Name="myTextBox" Text="{Binding BlueText}" Foreground="Blue" Width="100" Height="26" />
</Grid>
Here is the code behind of the UserControl:
public partial class BlueTextBox : UserControl
{
public BlueTextBox()
{
InitializeComponent();
DataContext = this; // shouldn't do this - see solution
}
public static readonly DependencyProperty BlueTextProperty =
DependencyProperty.Register("BlueText", typeof(string), typeof(BlueTextBox));
public string BlueText
{
get { return GetValue(BlueTextProperty).ToString(); }
set { SetValue( BlueTextProperty, value.ToString() ); }
}
This seems like it should be really easy, but I can't make it work. Thanks for your help!
More info: When i was trying the fix suggested by Eugene, I noticed some peculiar behavior. I added a PropertyChangedCallback to the metadata; this allows me to watch the value of BlueText getting set. When setting the string to a static value (="feeling blue") the PropertyChanged event fires. The data binding case does not fire PropertyChanged. I think this means the data-bound value is not getting sent to the UserControl. (I think the constructor does not get called in the static case)
Solution: The problems were correctly identified by Arcturus and jpsstavares. First, I was overwriting the data binding when is set DataContext=this in the constructor of the control. This prevented the data bound value from getting set. I also had to name the control x:Name=root, and specify the Binding ElementName=root int the XAML. To get the TwoWay binding, I needed to set Mode=TwoWay in the caller. Here is the correct code:
<src:BlueTextBox BlueText="{Binding Path=MyString, Mode=TwoWay}}" /> <!--OK-->
Now the XAML in the UserControl:
<UserControl x:Class="Binding2.BlueTextBox" x:Name="root"...
<Grid>
<TextBox x:Name="myTextBox" Text="{Binding ElementName=root, Path=BlueText}" Foreground="Blue" Width="100" Height="26" />
</Grid>
Finally I removed the DataContext=this in the constructor of the UserControl.
public BlueTextBox()
{
InitializeComponent();
//DataContext = this; -- don't do this
}
Thanks everyone for the tremendous help!
You set the DataContext in the Control to itself, thus overwriting the DataContext when using this Control in other controls. Taking your binding as example in your situation:
<src:BlueTextBox BlueText="{Binding Path=MyString}" />
Once loaded and all the Datacontext is set, it will look for the path MyString in your BlueTextBox thing control due to you setting the DataContext to it. I guess this is not how you intended this to work ;).
Solution:
Change the text binding either one of the 2 bindings:
{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type src:BlueTextBox}}, Path=BlueText}
or
Name your control Root (or something like that)
<UserControl x:Name="Root"
{Binding ElementName=Root, Path=BlueText}
And remove the
DataContext = this;
from the constructor of your UserControl and it should work like a charm..
I think in this case you need to set the ElementName property in the binding. Something like this:
<UserControl x:Class="Binding2.BlueTextBox" x:Name="blueTextBox"...
<Grid>
<TextBox x:Name="myTextBox" Text="{Binding ElementName=blueTextBox, Path=BlueText}" Foreground="Blue" Width="100" Height="26" />
</Grid>
Possibly you need to add to your property FrameworkPropertyMetadata where specify FrameworkPropertyMetadataOptions.AffectsRender and AffectsMeasure.
FrameworkPropertyMetadataOptions enumeration MSDN article
I know this is an old topic but still.
Also mention the PropertyChangedCallback on the UIPropertyMetadata during registering your DP

WPF: Cannot set properties on property elements weirdness

private TextBlock _caption = new TextBlock();
public TextBlock Caption
{
get { return _caption; }
set { _caption = value; }
}
<l:CustomPanel>
<l:CustomPanel.Caption Text="Caption text" FontSize="18" Foreground="White" />
</l:CustomPanel>
Gives me the following error:
Cannot set properties on property elements.
If I use:
<l:CustomPanel>
<l:CustomPanel.Caption>
<TextBlock Text="Caption text" FontSize="18" Foreground="White" />
</l:CustomPanel.Caption>
</l:CustomPanel>
My TextBlock shows up fine but it's nested inside another TextBlock like so, it even seems to add itself outside of the Caption property:
<l:CustomPanel>
<l:CustomPanel.Caption>
<TextBlock>
<InlineUIContainer>
<TextBlock Text="Caption text" FontSize="18" Foreground="White" />
</InlineUIContainer>
</TextBlock>
</l:CustomPanel.Caption>
<TextBlock>
<InlineUIContainer>
<TextBlock Text="Caption text" FontSize="18" Foreground="White" />
</InlineUIContainer>
</TextBlock>
</l:CustomPanel>
As you might have already guessed, what i'd like my code to do is to set my Caption property from XAML on a custom panel, if this is possible.
I've also tried the same code with a DependencyProperty to no avail.
So, anyone that can help me with this problem?
I can explain what is going wrong and how to fix it.
First,
<l:CustomPanel>
<l:CustomPanel.Caption Text="Caption text" FontSize="18" Foreground="White" />
is a simple syntax error. The <l:CustomPanel.Caption> syntax does not accept XML attributes - the property value must be within the element.
This is proper property element syntax:
<l:CustomPanel>
<l:CustomPanel.Caption>
<TextBlock Text="Caption text" FontSize="18" Foreground="White" />
</l:CustomPanel.Caption>
</l:CustomPanel>
but:
Property element syntax works only with DependencyProperties (so it didn't work with your CLR property) and
Property element syntax always honors the ContentPropertyAttribute of the property type
Since TextBlock has a [ContentPropertyAttribute("Inlines")], the property element syntax is trying to add the TextBlock to the Inlines collection.
The solution is simple: Declare your property as a DependencyProperty of type UIElement instead of type TextBlock. This has the additional advantage of not restricting the display of content to just a TextBlock. If you really do want to restrict it to just a TextBlock, you can use a validation callback.
public UIElement Content { get { ...
public static readonly DependencyProperty ContentProperty = ...
Just got a non-ideal workaround from a colleague of mine. It involves declaring the Caption property as a resource like:
<Page.Resources>
<TextBlock x:Key="test" Text="Caption text" FontSize="18" Foreground="White" />
</Page.Resources>
<l:CustomPanel Caption="{StaticResource test}" />
I'd still like to know why I can't use the two previous options, so if anyone knows please answer. :)
It seems that you can get this error (in Silverlight 4 and 5 at least) if you specify a namespace on the element. For example:
<Path>
<MapLayer.Position xmlns="clr-namespace:Microsoft.Maps.MapControl">
...
In this case MapLayer.Position is an attached property. It seems that the Silverlight parser requires the namespace to be defined using a prefix:
<Path xmlns:map="clr-namespace:Microsoft.Maps.MapControl">
<map:MapLayer.Position>
...

Categories

Resources