I was recently working with a lot of bindings to my configuration settings in XAML. Storing column widths/control sizes/window positions and the like. So I was wondering if there was an easy way to create and bind to "settings/configuration" values to XAML?
Right now I just create a setting in the project, shove a bindable property into the XAML's DataContext and go from there. But my settings count is getting pretty crazy and managing them is getting painful (boring, repetitive, and annoying).
In an ideal world I'd like a system where I can do something like this:
<Window State={Binding {Settings Name="MyWindowState", DefaultValue="Normal"}}/>
If the "MyWindowState" setting doesn't exist, it would be created automatically and stored somewhere. And if the MyWindowState setting changes, all the bindings that use it would also be notified and updated accordingly. And the DefaultValue would be used if the setting retrieval failed.
Does something akin to this exist already, or can it be achieved with the standard WPF XAML?
I am planning on working on something that can do this, but if a proven solution already exists I would love to at least look at it/hear it out.
From what I understand Telerik's persistance framework can do something like this, except on a control to control basis (there is no global "settings" I can bind to), at least on first glance.
Yes, it's quite possible. If you have an application properties file, you can access it like this:
Height="{Binding MainWindowHeight, Mode=TwoWay, Source={x:Static p:Settings.Default}}"
where MainWindowHeight is a setting (in my case, an int). You'll also need to include this in the top of your XAML file, in the Window or UserControl tag:
xmlns:p="clr-namespace:APPLICATION_NAME.Properties"
where APPLICATION_NAME is the name of your application.
EDIT: The binding can be have any mode, I just use TwoWay so I don't have to have any actual code to update it. For the positioning of my windows, it works out nicely that way.
EDIT: Also, this can't dynamically create settings. I would use an XML file in your application, make a class to handle that, and then bind to a method of the class to get/dynamically create the values.
You can do this with an attached property:
<Window loc:WindowState.Name="MyWindowState" />
In the OnNameChanged event handler of your attached property you will have access to the Window instance that the WindowState.Name property was set on and access to the value ("MyWindowState" in this example) that was set. There you start listening (e.g. using PropertyChangedEventManager) to changes of all properties of your Window instance that are part of your window state that you want to persist.
May be you can use WPF's theming option. You can store settings of your controls (width, color...) in a theme file, which is nothing but an xml file. You may store this xml somewhere and load it at runtime. You can update this xml when the application exits with the changes. And load it when the application opens next.
Related
I am converting my program from Winforms to WPF. With Winforms, it is easy to access a form from anywhere within code since the form seems to be static. However with WPF, I am finding this to be challenging. Example:
In WinForms, if I had a form with a text field, I could access it anywhere in code, and update the text field like so:
My.Forms.FormMain.txt_field.Value = "test"
In WPF, this same form is now a Window object and it seems the only way to call it globally is by using the following code:
Application.Current.MainWindow.txt_field.Value = "test"
The problem with using the Application.Current.MainWindow method is that this value is only available if the window is presented. My goal is to get a list of all of my Xaml Window objects and store them statically into global variables. This way if I need to update a property at a later time in code, I can easily do so.
So my question is, is this possible to accomplish in WPF?
There is a different way to implement WPF Window controls than WinForms, and that is through binding. To update the control's values, you have to set the Data Context of the Window with a view model. You have to implement a view model, which is a class file, implementing INotifyPropertyChanged interface. You have to declare properties here and notify the property changed event on their setter. Then bind those properties with your xaml controls. For details you can check 'Data Binding Overview' in WPF on msdn.
I have never done this, but u can try with Properties.
Something like
public string TexBoxValue
{
get {return txt_field.Value}
}`
But, i don't think this works with child window accessing parent window properties. Only other way around.
Other things u can do:
this way u can save your app important data and reused it after.
use Configuration Manager and store important in App.config file with Key - Value.
You can also create your own XML file with some of your app settings with your own tags!
It's easy and accessible from anywhere.
`
If I understand correctly, you are having trouble using Application.Current.MainWindow because it is typed as a WPF Window, which means you cannot refer to the controls on the window as members of the window.
The solution is to cast Application.Current.MainWindow to the type of the main window in your project (in the code below, MyMainWindow):
(Application.Current.MainWindow as MyMainWindow).txt_field.Value = "test'
The same applies to using windows via the Application.Current.Windows property, which holds a collection of all the open windows. Again, the Item property is typed as Window, so in order to use functionality defined in your windows, you have to cast them to the actual type of the window.
(Application.Current.Windows[0] as MyWindow).txt_field.Value = "test'
Right. So moving from WPF to UWP, I'm trying to use x:Bind to get compile-time benefits. Simple scenarios work fine; however I have found a number of issues that I was not able to solve. They are all related, so I thought I'd post them in one place:
I haven't been able to make Intellisense work with x:Bind. I have set DataContext (as well as d:DataContext just as we do in WPF) both in XAML and in the constructor, but it won't show members no matter what. Has anyone done this successfully?
Then I read somewhere that in UWP, DataContext is always set to Page's code-behind (really??) and that I need to define a ViewModel type property in the code-behind and then use that property in x:Bind. Is this correct? I tried it and it works but gives rise to the next question.
If I define a property of ViewModel type in Page's code-behind, Any sub-properties that raise PropertyChanged notifications do not update the UI. For example, if the code-behind property is named Game (of type GameVM) and there is a public property in GameVM named Player (of type GamePlayer), and in turn GamePlayer contains a property named Name, the x:Bind path will look like {x:Bind Path=Game.Player.Name}. But if I do this, any change notifications raised from within Name property do not update Page's UI.
One alternate I tried was to listen to PropertyChanged at each level and then bubble it up the hierarchy, but that hasn't worked. Even if it does, doing this seems a bit too much work. In WPF sub-properties like Game.Player.Name work properly without having to doing property change bubbling. Or am I missing something?
Right. After playing with it for a few days and searching numerous references, here are my findings:
{x:Bind} lacks design-time support. The feature is on the wishlist though. You may want to upvote it there.
(The new version of Visual Studio 15.4.4 does support Intellisense in {x:Bind}in the required way.)
{x:Bind} uses code-behind as its DataContext. So you need to define a public property of your ViewModel type in the code-behind and then use it in your {x:Bind} path.
As pointed out by IInspectable, the default mode for {x:Bind} is OneTime, unlike {Binding} which uses OneWay or TwoWay in almost all cases. So you need to explicitly specify Mode in your binding. People coming from WPF should take special care of it.
Sub-properties that implement notification change work perfectly fine in {x:Bind}. There is no need of bubbling these notifications upwards in the property hierarchy. The problem I was facing (#3 in the question) was because my sub-property was of type List<T>. I changed it to ObservableCollection<T> and it started working.
Hope this works somebody down the road.
Well as a beginner, the only question I can answer for you is the first one. Intellisense does not work inside the {x:Bind}. The members are never shown there in UWP for some unknown reasons. As for the next two questions of yours, I am still working on them.
I ran into the same challenge that you have seen. In my experience, in order to create the compile-time binding and have it update with custom objects as properties, the Page class seems to need to know about the data context and custom objects... all you need to do is reference them in the code behind, and then bind to them in the XAML. This creates the code generation objects it needs.
For example, I have a viewmodel, CustomerViewModel that is bound in XAML. That viewmodel also has a property of type IGuest. In order to use the guest object and have it update properly, I came up with this in the code behind...
CustomerViewModel vm
{
get
{
return (CustomerViewModel)DataContext;
}
}
IGuest g
{
get
{
return vm.CurrentGuest;
}
}
public CartGuestControl()
{
this.InitializeComponent();
}
You don't need to assign any of the UI data contexts from the code behind... simply reference the datacontext that is bound in XAML. When binding to any straight viewmodel properties, I use {x:Bind Path=vm.IsEditing, Mode=OneWay}. For binding to any of the guest properties, it looks like this, {x:Bind Path=g.FirstName, Mode=TwoWay}. You could do something like this for your Player object.
I have run into times where x:Bind simply won't do what I expect it to do no matter what I try. This can usually be solved by breaking things out into smaller user controls with more specific data contexts or by using "regular" Binding.
So, I have an UserControl which is basically a Grid with 3 different DataGrids and some Labels. Seeing how I need to use this 3 times, instead of copying and pasting the code, I thought I'd just generate it once and use it in my main window.
I have defined the UserControl as:
<UserControl x:Class="Propuestas.UI.Andrei.DGMTX"
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:Propuestas.UI.Andrei"
mc:Ignorable="d"
Height="300"
Width="791.496">
And I am using it in my window as such:
<StackPanel Grid.Row="2">
<local:DGMTX/>
<local:DGMTX/>
<local:DGMTX/>
</StackPanel>
For some reason, it doesn't show up in the designer panel on my main window. Is there something I'm doing wrong?
Also, I would like to be able to bind based on a bound element. For example, let's say I have a class Model which has all the data that I need to represent in my UserControl.
I would like to do something like
<local:DGMTX Binding = {Binding Model}/>
and then be able to bind all of the other elements in my UserControl in its code. Is there a way I could do this in XAML? Or do I have to do it programmatically?
There are two ways to communicate your view model to controls:
As one commenter suggested, bind your view model to the data context of the user control. This enables binding everything in your view model to the inner workings of the control. Problem is the inner workings now depend on the data the object is associated with.
Create dependency properties for only the ones in your view model that the user control actually needs. I personally prefer this over the first in almost 99% of all cases because you know exactly what data the control expects and you can manipulate bound data in ways unique to the control that maybe the view model isn't responsible for.
Couple things to note about designer support when creating your own controls:
Visual Studio's designer still has a lot of issues when it comes to WPF. Don't believe me? Try referencing a dynamic resource defined in your main assembly in another. The designer will crash and tell you it can't be found. This isn't the actual case, however. As soon as you run the app, you will never see this exception.
In order to see changes made to source reflect in designer, you have to build the project first (the project in which the control resides, not necessarily the one it's referenced in). Sometimes, "cleaning" or (with better luck in some cases) "rebuilding" the project is the only thing that updates the designer in the main project when "building" doesn't work.
If after considering the latter and you still can't see anything, consider the implementation of the control. Is anything out of place? Did something accidentally get hidden? You may not think so at first and maybe it takes ten hours of frustration to succumb and check, but the little things can make all the difference.
Having issues using a custom font in a WPF app.
To use the font, I set FontFamily="pack://application:,,,/Resources/#iconfont" on my label. However, whenever I go to edit any of the properties of the label in xaml, Visual Studio is automatically changing the font family pathway to FontFamily="iconfont", and the custom font no longer works. Any way to fix this? A setting I need to change? It's infuriating!
Edit: As long as I'm switching between properties of labels, FontFamily stays the same. But if I go to edit the properties of another type of control, and then come back to the label, FontFamily resets to "iconfont".
For starters let's make life easier and get your clutter cleaner by taking that long pack string and making it a defined resource in your resource dict like app.xaml, window.resources, wherever we can hit it from wherever we need.
So instead of putting FontFamily="pack://application:,,,/Resources/#iconfont" on every single instance it's used (which can be a performance hit by the way) we do;
<FontFamily x:Key="IconFont">
pack://application:,,,/Resources/#iconfont
</FontFamily>
We take that and plop it in your resource dictionary or wherever you want so it's referenced and loaded once from one place and made available to wherever else. Then at your instance you just do;
<Label FontFamily="{StaticResource IconFont}"/>
Now your editor should not only stop screwing with you but you have a cleaner way of maintaining that sucker. I mean imagine if you had to change your path string, or you decide to use a different font pack, would you rather do it on every instance, or in one spot that inherits to everywhere it's used?
Hope this helps, cheers.
Well, i must admit, still sometimes XAML seems a bit mysterious to me. The thing is, i always liked to debug through the C# code (setting lots of breakpoints in them) to get the idea of "what is happening" and "how is it happening". But with declarative XAML syntax that's not an option. I think you'll agree that to work with XAML, or to be precise, to work with/understand some existing XAML code you got to "already know" how things work with XAML declaration. There is just no way you can know/learn things investigating the execution of your application code. So i'm more than interested to take a look through XAML inside-out, as detailed as possible. I'm NOT talking about "learning" XAML, I know the basic stuff. May be i can provide some examples to clarify the sort of things i'm looking for -
Compared to C# code how an object gets instantiated when we use them in XMAL? Are they stored in managed heap? Same way as C# code-instantiated objects?
How the properties get set while using Mark-Up Extension syntax for Data/Command Binding?
When any property of an INotifyPropertyChanged type gets updated, how the Binding instatnce inside the XAML syntax updates the itself? How exactly it gets notified it at the first place, & by whom?
A viewmodel can be set as the DataContext of a view at runtime by defining Typed DataTemplate, like -
<DataTemplate DataType="{x:Type viewmodels:AccountsViewModel}">
<views:Accounts/>
</DataTemplate>
How does it happen actually? What are the rules for setting DataContext other than searching for the DataContext property upward the logical tree?
How the whole template things (DataTemplate, ControlTemplate & ItemsPanelTemplate) are treated/resolved at run time.
etc. etc. etc.
So if you are good/experienced/expert in XAML what would you suggest (links, articles, blogposts, books whatever) as reference that helps getting clear & deeper understanding about how XAML works "under-the-hood"? Thanks in advance.
Most can be explained by don't thinking of XAML as a real programming language, more like a declarative language. Everything you do in xaml, can be made in C# aswell, and in fact this is whats happening.
Compared to C# code how an object gets instantiated when we use them
in XMAL? Are they stored in managed heap? Same way as C#
code-instantiated objects?
Yes because they are just c# objects. Most resources are stored in a hibernated state, i rememberd the word inflated somewhere. Converter or other "direct" c# objects are created when they are needed. Important here is that these resources are usually shared, so they will get created only once.
How the properties get set while using Mark-Up Extension syntax for Data/Command Binding?
This again depends on where you use the markup extension. In a Style? In a Template? In a instanced user control like a window? Usually they are evaluated when you actually need them. It wouldn't make sense to evaluate them, when the inflated style is stored in the actual resource dictionary. They get evaluated when you actually use the style on an object.
When any property of an INotifyPropertyChanged type gets updated, how
the Binding instatnce inside the XAML syntax updates the itself? How
exactly it gets notified it at the first place, & by whom?
By the binding engine. WPF checks if your DataContext inherits the INotifyPropertyChanged interface, attaches to the event provided by the interface and listens to any changes. If such an event is raised, the binding engine will just call the getter again.
How does it happen actually? What are the rules for setting DataContext
other than searching for the DataContext property upward
the logical tree?
In short: None other. Datacontext is simply an inherited attached property. If you don't re set it on a child control, it will take the value the parent has until it reached the root. The only exception to this are ContentControls and ContentPresenter they will not inherit the DataContext but will change them depending on the content. So these controls always have by default the Content as their DataContext.
How the whole template things (DataTemplate, ControlTemplate & ItemsPanelTemplate) are treated/resolved at run time.
Simply spoken: Everytime WPF finds a non ui object, it tries to find a DataTemplate for the given type. In an ItemsControl for example: You can bind a list of MyClass; unless you provide an explicit DataTemplate or DataTemplateSelector it will search the resource tree upwards for an implicit style. Again remember that this already does not happen in XAML, but on the C# objects that was generated out of the xaml.
And is it by any means possible (at present or near future) to debug
through XAML code?
How do you think you can debug something that is not executed, but evaluated on compile time?
Please don't take this as 100% correct. Over the Years this is what i gathered of informations about XAML and the usage. If you have any corrections or find something that is clearly wrong. Please tell me, we are all here to learn and i always learn new things about the stuff i use :)