I have a single string constant that I have to re-use in several different XAML layouts, so instead of duplicating it, I'd like to just bind it to a constant.
I have a class which defines the string in C#:
public static class StringConstants
{
public static string MyString { get { return "SomeConstant"; } }
}
I'd like to be able to set the value through XAML via something like the following:
<Label Content="{Binding local:StringConstants.MyString}"/>
Is this achievable? I've searched for examples, but I've only found samples that involve some tinkering in the code-behind and I'm wondering if there's a simpler, XAML-only solution if I know that I just need to set the value once based on a string value that will never change.
You are binding to a static member so you should use x:Static Markup Extension:
<Label Content="{Binding Source={x:Static local:StringConstants.MyString}}"/>
According to #H.B.'s comment it's not necessary to use Binding so it's simpler to use:
<Label Content="{x:Static local:StringConstants.MyString}"/>
Put the public static string MyString in your App.xaml.cs. Then you can reference it as follows.
Content="{Binding Source={x:Static Application.Current}, Path=MyString}"
In the case that you have a constant inside of a non-static class, this doesn't work.
My solution for binding to a constant inside of a view model (MVVM).
It uses a getter property with less code for wrapping.
// view model
public const string MyString = "abc";
public string MyStringConst => MyString;
.
<!-- WPF -->
<Label Content="{Binding MyStringConst, FallbackValue='abc'}" />
The FallbackValue is used for the Designer preview.
Related
I have in a project called Common and there I have a Constants class:
public static class Constants
{
public class ListViewContextMenu
{
public const string ADD = "addToolStripMenuItem";
public const string RENAME = "renameToolStripMenuItem";
public const string DISABLE = "disableToolStripMenuItem";
public const string DELETE = "deleteToolStripMenuItem";
public const string ADD_TEXT = "Add";
public const string RENAME_TEXT = "Rename";
public const string DISABLE_TEXT = "Disable";
public const string ENABLE_TEXT = "Enable";
}
}
In other project, where is WPF, I want to use above constant in context menu header:
xmlns:constants="clr-namespace:Common;assembly=Common"
and
<ListView HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Column="0" Name="listView1" SelectionMode="Single" ItemsSource="{Binding TrackerList,Mode=TwoWay}" DisplayMemberPath="Title">
<ListView.ContextMenu>
<ContextMenu Name="lv_ctx">
<MenuItem Header="Add" Name="{x:Static constants:Constants+ListViewContextMenu.ADD}"></MenuItem>
<Separator/>
<MenuItem Header="{x:Static constants:Constants+ListViewContextMenu.RENAME_TEXT}" Name="{x:Static constants:Constants+ListViewContextMenu.RENAME}"></MenuItem>
<MenuItem Header="{x:Static constants:Constants+ListViewContextMenu.DELETE_TEXT}" Name="{x:Static constants:Constants+ListViewContextMenu.DELETE}"></MenuItem>
<MenuItem Header="{x:Static constants:Constants+ListViewContextMenu.DISABLE_TEXT}" Name="{x:Static constants:Constants+ListViewContextMenu.DISABLE}"></MenuItem>
</ContextMenu>
</ListView.ContextMenu>
</ListView>
But I get error:
MarkupExtensions are not allowed for Uid or Name property values, so
'{x:Static constants:Constants+ListViewContextMenu.ADD}' is not valid.
I expected to work since those are constants...
It is not a good idea to bind the Name property of an object (even if you are using Static Markup extension). Two reasons come to my mind: FindName() ContextMenu method won't work and you cannot refer the controls from your code-behind if their names are dynamic (i.e. they can change at run time), because the name is not known at building time.
I guess the exception that is thrown is meant to prevent those issues.
If you need to use those constants in your MenuItems, you can consider using Tag property. I hope it can help you.
x:Static is a markup extension.
https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/markup-extensions-and-wpf-xaml
I think it doesn't matter what x:Static is returning. The parser is checking for any markup extension and raising that error.
Whoever designed the parser decided that it was a good idea if the unique identifier for a ui object was right there in xaml as a string.
I can't see why you'd want to abstract Name in this way.
If the Name is set there in xaml then you can see it as you read the xaml. If your approach worked then you would need to go find the value in some other class.
All that abstracting seems to be achieving is making it harder to read the xaml. I see that as a negative, myself.
I think it's also worth explaining best practice.
Meaning MVVM for the vast majority of wpf dev teams.
A much more usual way of working would be to bind a collection of viewmodels to the itemssource of a menu. They would expose icommand and description bound to command and Header of the menuitem. No need to know the name of any menuitem clicked because it invokes the bound icommand.
I currently have an application where I'm parsing a YAML dictionary. I have a model named Line that looks like this -
public class Line
{
private ObservableDictionary<string, string> language = new ObservableDictionary<string, string>();
public Line()
{
Language = new Dictionary<string, string>();
}
// used with the YAML parser
[YamlMember(Alias = "language")]
public Dictionary<string, string> Language { get; set; }
}
You've probably noticed that I'm using an ObservableDictionary, which is not a standard type. I took the code from this other StackOverflow answer. As I understand it, it's just setting up the necessary INotifyPropertyChanged interface.
Anyway, for my Line, I have a ListView populated with a dictionary of translations represented as the language abbreviation and a textbox. To better exemplify what I'm talking about, here is a graphic.
In my App.xaml, I have a DataTemplate for my ListView defined - together, they look like this:
<ListView
ItemTemplate="{StaticResource LinesTemplateItem}"
ItemsSource="{Binding Value.Language}"
SelectionMode="None">
</ListView>
...
<DataTemplate x:Key="LinesTemplateItem">
<StackPanel Background="Transparent">
<TextBlock Text="{Binding Key}" />
<TextBox Text="{Binding Value, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
Everything seems like it should work fine. My data shows properly. However, when I change a value, it does not update the underlying source with the error:
Error: Cannot save value from target back to source.
BindingExpression: Path='Value' DataItem='Windows.Foundation.Collections.IKeyValuePair`2<String,String>';
target element is 'Windows.UI.Xaml.Controls.TextBox' (Name='null');
target property is 'Text' (type 'String').
From the error, I would guess that for some reason the databinding is targeting the entire UI control instead of the text inside of it. I've been searching for a while, but I can't seem to figure out how to fix this error. Any help is appreciated. Thanks.
The problem is that the property IKeyValuePair.Value is read-only so you cannot modify it. My proposition is to change you data model a little bit i.e. firstly create an additional class to store translations.
public class Translation
{
public string Expression { get; set; }
}
Now you should also change a definition of your dictionary e.g.:
public Dictionary<string, Translation> Language { get; set; }
Binding should be also updated accordingly:
<TextBox Text="{Binding Value.Expression, Mode=TwoWay}" />
Thanks to that if you change a value, the data binding will update Expression property which is not read-only.
I didn't test this code but I did similar things in the past so something like that should work.
Since you're binding to Dictionary<string,string>, each item is bound to KeyValuePair<string,string>, which is of value type - it's fields cannot be changed (while unboxed). You should bind to the pair itself, rather than its parts and use value converter to produce a pair with changed value.
At the very simplest level all I am trying to do is Data Bind TextBlock control (XAML). Am tying to get string from MyString (property defined in C# code behind) as a Text for TextBlock:
DisplayText disp = new DisplayText();
disp.MyString = "Hello";
public class DisplayText {
public string MyString {get;set;}
}
XAML code:
<TextBlock Grid.Column="1" Text="{Binding Path=MyString}" Foreground="Black"/>
But, Its not working:( Am searching for hours but could'nt get this simple thing done. Plz help!
In your XAML you need to define the DataContext.
For example:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Also, you'll need to implement INotifyChanged if you want the screen and model to stay in sync.
Have you seen the msdn article about data binding in Store Apps? http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh464965.aspx
The example code shows how to do what you are describing and worked for me.
I have a code in wich i need to be able to access to a different amount of prebuilt grids in XAMl and make them visible or collapsed
All grid are named like grid1,grid2,grid3 etc. I have the ability in code to obtain the string name via a random number and get the name od the grid i'd like to show.
I searched online and people suggest to use the reflect method, but i'm having a hard time trying to figure out the syntax that i have to use.
Best regards
The most straight forward way of doing this is to just declare a Name value for each Grid...:
<Grid Name="Grid1">
...
</Grid>
... and then you can access them by that name from the code behind:
Grid1.Visibility = Visibility.Hidden;
However, this is WPF and that is generally not recommended. A preferred method would be to add some bool properties to your code behind or view model...:
public bool IsGrid1Visible { get; set; } // Implement INotifyPropertyChanged interface
... and then to bind these directly to the Grid1.Visibility property using a BooleanToVisibilityConverter:
<Grid Grid1.Visibility="{Binding IsGrid1Visible, Converter={StaticResource
BooleanToVisibilityConverter}}">
...
</Grid>
Then you can change the Grid.Visibility value by simply setting the IsGrid1Visible property to true or false.
how can I do a bind if the property to show is a property from a property, like this case:
Xaml:
<TextBox Text="{Binding log.Message}"/> ????
In the class defined as Datacontext, I declare a log variable:
public Log log = new Log();
the Log class:
public class Log : INotifyPropertyChanged
{
public static string Message{ get { return message; } }
....
Your question is a bit unclear to me, but i give it a shot:
If the DataContext is an instance of the Log class, and the property is non static. Than the proper binding would be
<TextBox Text="{Binding Message}"/>
From there you can easily nest your bindings. For example if Log would have an instance of a class
public class Log {
public MessageHandler Message {get;set;}
}
which would have a property LocalizedMessage, it would simply be
<TextBox Text="{Binding Message.LocalizedMessage}"/>
If you want to bind to a static property, which your Message property currently is:
<TextBox Text="{Binding Source={x:Static MyNs:Log.Message}, Path=.}"/>
You can't bind static properties to XAML. Only .Net 4.5 enables that, and even that with some work. see: WPF 4.5 – Part 9 : binding to static properties. You can find the way there.
If you can't use .Net 4.5, check out this SO thread for another workaround.
The problem with what you wrote is that Message is a static property, so you're not suppose to get it from the log object, but from the Log class:
<Window.Resources>
<local:Log x:Key="logClass"/>
</Window.Resources>
<TextBox Text="{Binding Source={StaticResource logClass}, Path=Message}"/