Scenario
I can't seem to find a way to format bindings in WPF using an IFormatProvider. Currently I've a property on my data source:
public int PersonNumber { get; set; }
This property is bound to a Label in XAML:
<Label Content="{Binding Path=PersonNumber}" />
As you can see it's a number, but should be formatted like 0000.00.000. Currently we use a separate IFormatProvider for such things in our old WinForms application.
Questions
Is formatting like this possible in WPF?
If yes, our preferred way is to still use an IFormatProvider, also possible?
If no, what is a good alternative?
Thanks in advance!
You're looking for the ContentStringFormat property, which is on all ContentControl descendants including Label.
<Label Content="{Binding PersonNumber}" ContentStringFormat="000" />
I'm not sure whether WPF's formatting can make use of an IFormatProvider, though.
And to be complete, you could just add a String property to your ViewModel to get full control in C#:
public int PersonNumber { get; set; }
public string PersonNumberText { get { return ... } }
You can use the property StringFormat of Binding
Nice example: http://blogs.msdn.com/b/mikehillberg/archive/2008/05/29/trying-out-binding-stringformat.aspx
In addition to the posted answers, there's always available the converter way.
Related
In part of a XAML code maximum and minimum values are set as follows:
<WindowsFormsHost>
<wf:NumericUpDown Maximum="12000" Minimum="120" x:Name="MyNumericUpDown" TextAlign="Right"/>
</WindowsFormsHost>
And in the same C# program inside a class I have the following:
namespace MyNameSpace
{
public class MyClass
{
public int max { get; } = 12000;
public int min { get; } = 120;
...
}
}
Is it possible to set the Maximum and Minimum values of NumericUpDown by using the class properties instead of hardcoding them? So that of I change class property values the XAML values autonomically updated.
Yup, it's possible, and even what I'd consider to be fundamental in WPF development. It's called data binding. I'll be implementing a simple one way data binding here which binds to a property on the code behind file (i.e MainView.xaml binds to MainView.xaml.cs), but you can bind to any class you'd like, this then forms the fundamentals of MVVM.
Xaml:
<Window x:Class="MainView"
Title="MainView"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<!-- Snip -->
</Window>
The important part is DataContext="{Binding RelativeSource={RelativeSource Self}}. Right here we're telling the window to bind to itself (i.e the code behind file). This is required for anything to work. You can use this Q&A to see how to bind to something else.
Continuing on:
<wf:NumericUpDown Maximum="{Binding Max}" Minimum="{Binding Min}" />
Here we're telling the NumericUpDown component to get its Maximum and Minimum values from the data context. Now, one caveat, you can only bind to properties and nothing else. So, if you ever run into a problem where your bindings aren't working, check to see if you're binding to a property or not. Here's the code of code behind file
public class MainWindow
{
public int Max { get; } = 12000;
public int Min { get; } = 120;
public MainWindow()
{
InitializeComponent();
}
}
This is a one way data binding, i.e the XAML can only read these values. For things like an input box, you'll need to use TwoWay bindings. And if anything changes those values (currently impossible, but may be in the future), then you need to implement INotifyPropertyChanged
P.S: No guarantees for the code. I just typed it out in this text box without any auto complete and the last time I did WPF is quite a while ago. Just leave a comment if something doesn't work and I'll try my best to fix it
View:
<Picker ItemsSource="{Binding ECCLevels}"
ItemDisplayBinding="{Binding QRCodeGenerator.ECCLevel}"
HeightRequest="44"/>
ViewModel:
public ObservableCollection<QRCodeGenerator.ECCLevel> ECCLevels { get; set; } = new ObservableCollection<QRCodeGenerator.ECCLevel>();
ECCLevels = new ObservableCollection<QRCodeGenerator.ECCLevel>(Enum.GetValues(typeof(QRCodeGenerator.ECCLevel)).OfType<QRCodeGenerator.ECCLevel>().ToList());
I simplified the code above. The ObservableCollection gets filled with data after the second line of code. But the problem is I don't know what to put in the ItemDisplayBinding property of the picker since there the Enum gets converted into a list directly without going through a model.
You can just use ItemDisplayBinding="{Binding .}", the dot notation means that you refer to this basically. So, this way you simply reference the object itself and not any other property.
In the comments you asked me how I know, that is actually I great question. I think I picked it up as early as WPF. But it's hard to dig up from the documentation, I found a small notice of it here: https://learn.microsoft.com/en-us/dotnet/api/system.windows.data.binding.path?redirectedfrom=MSDN&view=netframework-4.7.2#System_Windows_Data_Binding_Path
Optionally, a period (.) path can be used to bind to the current source. For example, Text="{Binding}" is equivalent to Text="{Binding Path=.}".
I have been searching everywhere for this. Just as the title says, is there a way for a XamlWriter to read the content of a text box, as well as the definition of the text box itself?
The text boxes are not directly entered into the Xaml, as they have been binded to the MVVM pattern, at runtime. At the moment, the XamlWriter simply saves the text boxes without their content.
Example code saving the view, but without text box content:
string mystrXAML = XamlWriter.Save(StackPanelContent);
string name = txtname.Text;
FileStream fs = File.Create(String.Format(#"C:\Desktop\" + name + ".txt"));
StreamWriter sw = new StreamWriter(fs);
sw.Write(mystrXAML);
sw.Close();
fs.Close();
EDIT:
Have spent alot of time thinking and researching how to do this still cant find the right answer. This is my XAML code and its still not saving the text box content and this is because they are binded (Is what i researched)
XAML:
<ListView ItemsSource="{Binding Title}" ItemTemplate="{DynamicResource Template}">
<ListView.ItemContainerStyle>
<Style>
<Setter Property="FrameworkElement.Margin" Value="20,20,0,0"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.Resources>
<DataTemplate x:Key="Template">
<TextBox Text="{Binding .}" Width="200" HorizontalAlignment="Left" />
</DataTemplate>
</ListView.Resources>
</ListView>
<ListView x:Name="QuestionList" ItemsSource="{Binding Question}" ItemTemplate {DynamicResource Template}">
<ListView.ItemContainerStyle>
<Style>
<Setter Property="FrameworkElement.Margin" Value="40,20,0,0"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.Resources>
<DataTemplate x:Key="Template">
<TextBox Text="{Binding .}" Width="200" HorizontalAlignment="Left"/>
</DataTemplate>
</ListView.Resources>
</ListView>
I just don't understand you... you come to this site and ask for helpful advice and when that helpful advice is given to you, you ignore it and ask your (practically same) question again and again. You've already been told by several different users that in WPF, we work with data, not UI controls. So rather than save the TextBoxes with all their extra properties that are meaningless to you, you should save the just text in the collections data bound to the TextBoxes.
Now I hope that you don't take this personally again, but at some point, you have to start listening to the people that know what they're talking about. However, if your heart is set on doing this in your own stubborn way, rather than the correct way, then may I draw your attention to the XamlWriter Class page on MSDN:
The serialization enabled by this method has a series of limitations. This is because the serialization enabled is explicitly run-time, and does not have access to possible design-time information in the original XAML (if any). For details, see Serialization Limitations of XamlWriter.Save.
And from the linked page on MSDN:
Common references to objects made by various markup extension formats, such as StaticResource or Binding, will be dereferenced by the serialization process. These were already dereferenced at the time that in-memory objects were created by the application runtime, and the Save logic does not revisit the original XAML to restore such references to the serialized output. This potentially freezes any databound or resource obtained value to be the value last used by the run-time representation, with only limited or indirect ability to distinguish such a value from any other value set locally.
I've highlighted a section that basically says that yes, you can save the property values of the controls using the XamlWriter.Save method. However, I can't tell you why your values aren't being saved.
Now when you said that you had searched everywhere, you seem to have missed the most obvious search terms for your problem: XamlWriter Class. For future reference, had you searched with these words, then you'd have found all this out in the top result.
However, it just makes far more sense to save the data and not the UI controls. Think about it... you already have the mechanism to generate/display these TextBoxes from your collections, so why wouldn't you just use that? Save the data in a database, or a text file and when you reload it, use your existing code to re-populate the TextBoxes from the collections. It really is as simple as populating a collection in your view model with the loaded data. You could even save the data collection with almost the same code that you're using already, or less even.
That is how it is done properly. Now you can either accept that and move on with your project in the correct manner, or you can continue to ignore this advise and to ask more virtually identical questions. Right now, I think that this is at least your 3rd question asking how to store XAML in a file or database. You could have saved everyone including yourself a lot of time if you had just accepted the good advise that was given to you previously, or even done a proper search.
Apart from anything else, it's just so much easier persisting the data instead of the UI controls.
Finally, did you notice how your (very similar) question had not been answered until now? Have you seen how most questions are normally answered immediately here? I'm not the only person to notice all of your very similar questions and this shows you that the StackOverflow community as a whole do not like it when users abuse this website by expecting others to do all of their work for them.
Now you probably won't believe this as we've 'clashed' before, but all of this advice is meant to help you. Apologies if any of my comments have offended you, but if you use this site according to the rules, then you'll get much more out of it. For example, rather than asking the same question again and again, just edit your original question if you didn't get a satisfactory answer. An edited question gets posted back to the top of the unanswered queue again to attract more attention.
I hope you'll accept my advice this time and wish you luck with your project.
UPDATE >>>
The linked pages from MSDN show us that the XamlWriter can save data bound data, but unfortunately, I can't tell you why yours is not working.
To be honest, saving strings is not really enough. You need to save all of the relevant data. The simplest way to display, edit and save this data is to create custom classes that implement the INotifyPropertyChanged interface. For example, for you scenario, I would create a Question class something like this:
[Serializable]
public class Question // Implement INotifyPropertyChanged correctly here
{
public string Title { get; set; }
public string Text { get; set; }
public string Answer { get; set; }
}
If your questions are multiple choice, then you could do something like this:
[Serializable]
public class Question // Implement INotifyPropertyChanged correctly here
{
public string Title { get; set; }
public string Text { get; set; }
public List<Answer> Answers { get; set; }
public Answer CorrectAnswer { get; set; }
}
Of course, you should have private fields backing those public properties... this is just a simplified example. Note the use of the SerializableAttribute... this will enable you to save the data with just a few lines of code.
You can structure the properties however you want, but the idea is that you encapsulate all of the related data into one class. Then you can provide a DataTemplate for that class type, let's call it Template, and define a collection of controls that will display the relevant property values.
Now here's the good part... you can create a collection of that type in a view model...:
public ObservableCollection<Question> Questions { get; set; }
... and set the ItemTemplate property of any collection control to your DataTemplate:
<ListBox ItemsSource="{Binding Questions}" ItemTemplate="{StaticResource Template}" />
and then every Question item in the collection will magically be rendered exactly as you defined in the DataTemplate. So to answer your question about how do you reload the data, you just re-populate the Questions collection in the view model and the WPF templating system will correctly re-populate the UI for us in exactly the same way as it did before that data was saved.
Now as you can see... I've spent a great deal of time explaining this all to you. I trust that you can answer any further questions that you may have with your new found search skills.
To start you off, here are some useful articles:
Serialization (C# and Visual Basic)
Walkthrough: Persisting an Object (C# and Visual Basic)
Data Templating Overview
INotifyPropertyChanged Interface
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.
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.