I am developing a text editor with WPF which should also be usable for blind people.
For screenreaders to read the program interface, the Helptext property must be set for components.
In XAML, this is how it works:
AutomationProperties.HelpText = "Here the accessibility of the editor can be activated or deactivated"
But how do I set the property HelpText in C#?
Attached properties conventionally have a static method that is the property name prefixed with "Set" to update from code. In this case
SetHelpText(DependencyObject, value)
AutomationProperties.SetHelpText(ChkBarrierefrei, "Here the accessibility of the editor can be activated or deactivated")
As you've asked for an example on how to use bindings to achieve your goal, here is a simple one.
We first need to create a class that works as our base class for every so-called ViewModel.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
We can now derive specific ViewModels from that class, namely the one we want to use to hold the BarriereFrei Help Text.
public class MyViewModel : ViewModelBase
{
private string barriereFreiHelpText = "Here the accessibility of the editor can be activated or deactivated.";
public string BarriereFreiHelpText
{
get { return this.barriereFreiHelpText; }
set
{
if (value == this.barriereFreiHelpText)
{
return;
}
this.barriereFreiHelpText = value;
this.RaisePropertyChanged(); // This line makes sure the UI is updated
// whenever a new help text is set.
}
}
}
In the View (I assumed it's called MainWindow) we can now use the previously defined ViewModel like shown below:
<Window x:Class="MyProject.Views.MainWindow"
xmlns:viewModel="clr-namespace:MyProject.ViewModels">
<Window.DataContext>
<viewModel:MyViewModel />
</Window.DataContext>
<Grid>
<CheckBox x:Name="ChkBarrierefrei"
Content="Editor ba_rrierefrei"
ToolTip="Hier kann die Barrierefreiheit des Editors aktiviert oder deaktiviert werden"
AutomationProperties.HelpText="{Binding BarriereFreiHelpText}"
Margin="10"
Click="ChkBarrierefrei_CheckedChanged"/>
</Grid>
</Window>
Did you notice the
AutomationProperties.HelpText="{Binding BarriereFreiHelpText}"
part? This is where the magic happens. What this line does is it ensures that the HelpText property is always equal to what you set in the ViewModels BarriereFreiHelpText property.
To make this all work correctly, somewhere in your application you need to wire up the ViewModel and the Window like this:
MyViewModel viewModel = new MyViewModel();
MainWindow window = new MainWindow();
window.DataContext = viewModel;
and you can now change the help text at runtime using
(window.DataContext as MyViewModel).BarriereFreiHelpText = "Whatever you want it to be";
and the UI is automatically updated with the new help text.
This is definitely a better solution than setting the CheckBoxes AutomationProperties.HelpText property directly, as it is much cleaner and less tightly coupled (you are changing the property of a ViewModel without knowing about any UI).
I understand that this all might be overwhelming, but I strongly recommend you follow the so-called MVVM approach, not only for the CheckBoxes help text, but for everything at all. You will save yourself much time, lines of code and nasty bugs.
Further reading:
MVVM und WPF (german) explains how to use MVVM and is way more detailed and easier to understand than my answer,
The World's Simplest C# WPF MVVM Example is a very nice, short and comprehendible article covering everything you need to know in order to create robust and reusable WPF applications,
MVVM Step by Step from Basic to Advanced another nice article, covering why MVVM makes sense.
I hope it's not to late to go with the MVVM approach, and I wish you lots of success and fun with MVVM - it really is nice and fun once you got a hang of it, believe me ;)
Related
I'm working on an app that has a "day" and "night" color palette that can change automatically. We're using Xamarin Forms and, for historical reasons, we're not using XAML but I speak XAML so I'm going to use it in this post.
I've approached it by creating a base type with a property for relevant colors like "dark text" or "header background", then implementing that type for both schemes. Then, I made a type that references one of those and raises a PropertyChanged even if it changes. So a day->night transition involves setting the property, then anything in the UI bound to a path like "ColorScheme.DarkText" changes. Nice.
Now I've got a ViewModel that wants to have different colors for some items in a list. I want those colors backed by this day/night change system, but I might have designed myself into a corner. I'll show you what I did and how I want to redesign, but I'm curious if there's a clever way to go about it without causing other problems.
Here's a VM for an item I'm binding to, let's all assume there's nothing unexpected in ViewModelBase:
public class ItemViewModel : ViewModelBase {
public string IconColorName { get...; set...; }
public string IconText { get...; set...; }
}
That ViewModel's contained in another boring ViewModel that makes up the rest of the page:
public class PageViewModel : ViewModelBase {
public ObservableColorScheme ColorScheme { get...; set...; }
public ObservableCollection<ItemViewModel> Items { get...; set...; }
}
OK, so what I'm going for is I'd like XAML for my item's template to look something like:
<StackLayout>
<StackLayout.Children>
<Label TextColor={Binding IconColor, Converter={StaticResource StringToColorConverter} />
...
</StackLayout.Children>
</StackLayout>
Right. OK. So now here's the problem. I can imagine building that IValueConverter and setting it up so it has the same concept of the right color scheme, then using the string value here to get the appropriate property. But I have a problem: there can only be one source for a binding, right? I need the color to change if EITHER the ColorScheme or IconColorName changes. My hunch is WPF could do that, but Xamarin can't?
The most obvious solution I've thought of is some kind of extra ViewModel, in XAML-unfriendly format for brevity:
public class ColorViewModel : ViewModelBase {
public Color Color { get...; set...; }
public ColorViewModel(string colorName, ObservableColorScheme colorScheme) {
colorScheme.PropertyChanged += (s, e) => {
if (e.PropertyName == colorName) {
Color = colorScheme.Get(colorName);
}
}
Color = colorScheme.Get(colorName);
}
}
I do NOT like this. These items are created and destroyed a lot, so that means that event handler needs to be unsubscribed. I don't want to have to think about that, and I can assume a maintenance programmer will forget. I've thought about retooling it to use a WeakReference for the event subscription but... that's getting really icky.
So I'm not really sure how to proceed, short of making the Page here detect color scheme changes and manually update its child views. That feels icky too. I've been thinking about it for a couple of days and nothing nice is presenting itself.
I'm open to "you're doing this terribly wrong, and there's some feature that would make this dramatically easier for you". I'm suspicious that feature is Styles, which I'm not using because 1) the aforementioned lack of using XAML and 2) our project is older than Style support in Xamarin Forms. Feel free to tell me to throw this design away, but please don't do so without showing me a quick example of the better way!
I don’t know xamarin nor C#, so maybe I’ll use the wrong terms, but this is how I would approach this problem:
I assume you have some kind of ViewModel hierarchy and you know the root of this hierarchy.
I would create a ColorScheme class with a static getter of the current color scheme and getters for each color. So you can create a subclass for day and night scheme. Also a void ApplyTo(ViewModelBase). I would create an ViewModelBase interface which has an void UpdateColorScheme() and List Items().
Ok, now, each ViewModel can use the UpdateColorScheme method to setup the view will be created.
Probably there is some kind of event to change the color scheme. This could be a button or a clock based trigger. This event sets the correct ColorScheme and simply calls ColorScheme.CurrentScheme().ApplyTo(rootViewModel). The ApplyTo method walks down the ViewModel hierarchy and calls UpdateColorScheme() for each ViewModel.
This isn’t very fancy but your don’t have to create dozens of objects for something which will change rarely (only twice a day). You only have one instance of ColorScheme and be instance for each Color and a separate method for color settings. But you don’t have to create and register events listeners all the time. The base class will enforce the maintenance programmer to use this concept so he can not forget to setup a event listener and you use the same code for setting up and updating the view.
And a little side note: opinion based questions are not allowed on Stack Overflow. There is a Core Review Page of StackOverflow, I think this question belongs there.
Edit: i have simplified this problem a lot since i first wrote it up. See the code examples at the bottom. I will clean the post up tomorrow.
I want to set the DataContext AFTER the user as filled out a form because that is when I know the type of object I need to create (based on what the user selected). The problem with doing it this way is that the BindingGroup.IsDirty flag is false because all edits are done by this point and I suppose that is why my call to BindingGroup.UpdateSources is having no effect.
Essentially this is what I'm doing:
Have user fill out form. The DataContext has not been set yet, but bindings are in place.
In the buttonSave_Click handler, create and set the correct DataContext object (based on provided user input) then call BindingGroup.UpdateSources and close the dialog.
I realize there are other ways to go about this. Probably even better ones, but I am really just experimenting with DataBinding and trying to learn it better. That is why I am trying out many possible designs with this.
Ultimately I will probably settle on a design where I ask the questions that I need answered in order to know what object to create for the data context then set it and let them fill out the remainder of the form from there. But for now I want to get it working the other way (if it is even possible) just for learning purposes.
EDIT 1:
I have determined that the IsDirty flag must not be my problem because immediately before the DataContext is set, IsDirty is true. Then immediately after DataContext is set it becomes false so I suppose it automatically updated sources then, however, I do not see the changes reflected in my data object so it obviously failed for some reason.
I know my Bindings are correct because if I set the DataContext in the Windows ctor it updates the data. Move those two lines of code to the buttonSave_Click handler and it no longer updates data.
I feel like there is something that I am missing that is going to me make me feel reaaallly stupid when I figure it out :\
UPDATE 1:
I have determined that the BindingExpression.Worker.CanUpdate is false. By looking at the source code of BindingExpression I can see that that would cause it to fail. Now to figure out why that is false....
UPDATE 2:
With all the reading i have been doing i am starting to suspect a timing issue. read this
I will be able to try it when i get home
UPDATE 3:
Still working on this. Here is a bit of code. I have simplified to get minimal code.
Here is the data objects.
public abstract class Dog
{
public Dog(SexType sex)
{
NickName = string.Empty;
}
public string NickName { get; set; }
}
public class Stud : Dog
{
public Stud()
: base(SexType.Male)
{
}
}
This code works:
//code behind
public partial class AddDogWindow : Window
{
public Dog NewDog { get; set; }
public AddDogWindow()
{
InitializeComponent();
//set the DataContext here and the data object will be updated as expected...
NewDog = new Stud();
DataContext = NewDog;
}
private void buttonSave_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
}
}
And the XAML
<Window x:Class="PuppyMan.AddDogWindow"
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:PuppyMan"
mc:Ignorable="d"
Title="AddStudDogWindow" Height="300" Width="300">
<StackPanel>
<TextBox HorizontalAlignment="Stretch" Text="{Binding NickName, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<Button x:Name="buttonSave" Click="buttonSave_Click">Save</Button>
</StackPanel>
</Window>
THIS is the code that BREAKS.
public partial class AddDogWindow : Window
{
public Dog NewDog { get; set; }
public AddDogWindow()
{
InitializeComponent();
}
private void buttonSave_Click(object sender, RoutedEventArgs e)
{
//Notice all I have changed is moving the set DataContext out of the ctor and into this handler.
//Now the data object no longer gets updated.
NewDog = new Stud();
DataContext = NewDog;
DialogResult = true;
}
}
The idea is that I let them fully fill out the dog info and then I only create the Stud or Dame dog object based on the final decision of whether the dog is male or female. A kind of lazy data binding I suppose. I know this isn't the best design for this situation but I want to learn how to do it this way for learning purposes. Or learn why this method won't work, either is fine, I just want to learn this data binding stuff and well!
FINAL UPDATE
This post seems to be essentially the same as my problem. I am using OneWayToSource and it always sets my NickName property to the default value (in this case ""). The problem is NOT that the NickName getter is called after the setter, but that it gets set to "". The UI keeps the original value until I type in the TextBox again and INotifyPropertyChange fires then everything syncs up again.
Seems an awful lot like a bug to me that OneWayToSource would push a "" instead of the current value of the target. But I very well may be miss understanding.
Design your form such that you know beforehand which object you will get.
This can probably be done for example by checking if uses fills a particular field, or if he selects some combobox value in your form. In appropriate event of that control you can set your datacontext. And by the way Triggers are most suited here. Set trigger for your object-deciding-form-field. More can be done if you post something more, or upload some source code at dropbox.com.
//////////
You have set Bindings beforehand, and then you are changing the DataContext. Changing the DataContext will not make your TextBox value to reach the NickName property.
Secondly, you have set UpdateSourceTrigger to PropertyChanged. So, after changing your DataContext you have to make this property appear changed.
One way to do this is to :
DataContext = NewDog;
/* this will trigger property changed */
txtDogNickName.Text = txtDogNickName.Text;
string newNickName = NewDog.NickName; // updated value appear here
But this is not the proper way to do. Change your DataContext sepearately, and then edit values in your form. Changes will propagate correctly. Don't do everything in same handler. For example, you might show this form in some Button1's click handler and set DataContext, then use another Button2's click handler to get form values.
My problem turned out to be the same issue as this post. I am using OneWayToSource and when I change the DataContext my property is set to a default value which is currently "" instead of the value of the target as I was expecting. I have found no work around. I will design such that I know what object to create in advance so that the DataContext will be set in the constructor.
I have a user control written in C# & WPF using the MVVM pattern.
All I want to do is have a property in the bound ViewModel exposed to outside of the control. I want to be able to bind to it and I want any changes to the property to be picked up by anything outside the control that is bound to the exposed value.
This sounds simple, but its making me pull out my hair (and there is not much of that left).
I have a dependency property in the user control. The ViewModel has the property implementing the INotifyPropertyChanged interface and is calling the PropertyChanged event correctly.
Some questions:
1) How do I pick up the changes to the ViewModel Property and tie it to the Dependency Property without breaking the MVVM separation? So far the only way I've managed to do this is to assign the ViewModels PropertyChanged Event in the Controls code behind, which is definitely not MVVM.
2) Using the above fudge, I can get the Dependency property to kick off its PropertyChangedCallback, but anything bound to it outside the control does not pick up the change.
There has to be a simple way to do all of this. Note that I've not posted any code here - I'm hoping not to influence the answers with my existing code. Also, you'd probably all laugh at it anyway...
Rob
OK, to clarify - code examples:
usercontrol code behind:
public static DependencyProperty NewRepositoryRunProperty = DependencyProperty.Register("NewRepositoryRun", typeof(int?), typeof(GroupTree),
new FrameworkPropertyMetadata( null, new PropertyChangedCallback(OnNewRepositoryRunChanged)));
public int? NewRepositoryRun
{
get { return (int?)GetValue(NewRepositoryRunProperty); }
set
{
SetValue(NewRepositoryRunProperty, value);
}
}
private static void OnNewRepositoryRunChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.OldValue != e.NewValue)
{
}
}
public GroupTree()
{
InitializeComponent();
GroupTreeVM vm = new GroupTreeVM();
this.DataContext = vm;
}
Viewmodel (GroupTreeVM.cs)
private int? _NewRepositoryRun;
public int? NewRepositoryRun
{
get
{
return _NewRepositoryRun;
}
set
{
_NewRepositoryRun = value;
NotifyPropertyChanged();
}
}
And now for my weekly "don't do that" answer...
Creating a ViewModel for your UserControl is a code smell.
You're experiencing this issue because of that smell, and it should be an indication that you're doing something wrong.
The solution is to ditch the VM built for the UserControl. If it contains business logic, it should be moved to an appropriate location in another ViewModel.
You should think of a UserControl as nothing more than a more complex control. Does the TextBox have its own ViewModel? No. You bind your VM's property to the Text property of the control, and the control shows your text in its UI.
Think of UserControls in MVVM like this--For each model, you have a UserControl, and it is designed to present the data in that model to the user. You can use it anywhere you want to show the user that model. Does it need a button? Expose an ICommand property on your UserControl and let your business logic bind to it. Does your business logic need to know something going on inside? Add a routed event.
Normally, in WPF, if you find yourself asking why it hurts to do something, it's because you shouldn't do it.
Perhaps I've misunderstood, but it seems like you're trying to use binding in the code behind?
public MyUserControl()
{
InitializeComponent();
// Set your datacontext.
var binding = new Binding("SomeVMProperty");
binding.Source = this.DataContext;
SetBinding(MyDependencyProperty, binding);
}
I am aware there are a couple of questions similar to this one, however I have not quite been able to find a definitive answer. I'm trying to dive in with MVVM, and keep things as pure as possible, but not sure how exactly to go about launching/closing windows while sticking to the pattern.
My original thinking was data bound commands to the ViewModel triggering code to start a new View, with the View's DataContext then set to it's ViewModel via XAML. But this violates pure MVVM I think...
After some googling/reading answers I came across the concept of a WindowManager (like in CaliburnMicro), now if I was to implement one of these in a vanilla MVVM project, does this go in with my ViewModels? or just in the core of my application? I'm currently separating out my project into a Model assembly/project, ViewModel assembly/project and View assembly/project. Should this go into a different, "Core" assembly?
Which leads on a bit to my next question (relates somewhat to the above), how do I launch my application from an MVVM point of view? Initially I would launch my MainView.xaml from App.xaml, and the DataContext in the XAML would attach the assigned ViewModel. If I add a WindowManager, is this the first thing that is launched by my Application? Do I do this from the code behind of App.xaml.cs?
Well it mainly depends on how your application looks like (i.e. how many windows opened at the same time, modal windows or not...etc).
A general recommendation I would give is to not try to do "pure" MVVM ; I often read things like "there should be ZERO code-behind"...etc., I disagree.
I'm currently separating out my project into a Model assembly/project,
ViewModel assembly/project and View assembly/project. Should this go
into a different, "Core" assembly?
Separating views and ViewModels into different assemblies is the best thing you can do to ensure you won't ever reference something related to the views in your viewModel. You'll be fine with this strong separation.
Separating Model from ViewModel using two different assemblies could be a good idea too, but it depends on what your model looks like. I personally like 3-tier architectures, so generally my model is the WCF client proxies and are indeed stored in their own assembly.
A "Core" assembly is always a good idea anyway (IMHO), but only to expose basic utility methods that can be used in all the layers of your application (such as basic extension methods....etc.).
Now for your questions about views (how to show them...etc), I would say do simple. Personally I like instantiating my ViewModels in the code-behind of my Views. I also often use events in my ViewModels so the associated view is notified it should open another view for example.
For example, the scenario you have a MainWindow that should shows a child window when the user click on a button:
// Main viewModel
public MainViewModel : ViewModelBase
{
...
// EventArgs<T> inherits from EventArgs and contains a EventArgsData property containing the T instance
public event EventHandler<EventArgs<MyPopupViewModel>> ConfirmationRequested;
...
// Called when ICommand is executed thanks to RelayCommands
public void DoSomething()
{
if (this.ConfirmationRequested != null)
{
var vm = new MyPopupViewModel
{
// Initializes property of "child" viewmodel depending
// on the current viewModel state
};
this.ConfirmationRequested(this, new EventArgs<MyPopupViewModel>(vm));
}
}
}
...
// Main View
public partial class MainWindow : Window
{
public public MainWindow()
{
this.InitializeComponent();
// Instantiates the viewModel here
this.ViewModel = new MainViewModel();
// Attaches event handlers
this.ViewModel.ConfirmationRequested += (sender, e) =>
{
// Shows the child Window here
// Pass the viewModel in the constructor of the Window
var myPopup = new PopupWindow(e.EventArgsData);
myPopup.Show();
};
}
public MainViewModel ViewModel { get; private set; }
}
// App.xaml, starts MainWindow by setting the StartupUri
<Application x:Class="XXX.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
...
StartupUri="Views/MainWindow.xaml">
I am new to WPF and a question arose in my mind while I was developing my app.
Assume that I have a textbox defined as below
<TextBox x:Name="MyTextBox" />
Then in my C# code, I can change the string shown in this textbox using the following command
MyTextBox.Text = "Hello!";
However, there is another way to have the same behavior by use of binding when in XAML we have
<TextBox x:Name="MyTextBox" Text="{Binding Content}" />
and in C# we have
public class MyText : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _content;
public string Content
{
get { return _content; }
set
{
_content = value;
PropertyChanged(this, new PropertyChangedEventArgs("Content"));
}
}
}
MyText txt = new MyText();
MyTextBox.DataContext = txt;
txt.Content = "Hello!";
Obviously the second option needs more coding, but the result of both of them are the same. However, in second case I do not have to care about executing the code on UI thread. So everywhere in my code when I change txt.Content the string in the textbox will change without any exception.
My question is: Is there any design issue preferences over any of these two options for changing a property?
The second option is a prerequisite for employing MVVM pattern.
In the first option there is no decoupling; every operation takes place in the view.
But if you prefer the section option and apply MVVM pattern, you will have two different classes; one for implementing only UI - namely View, and one for abstracting the View and the Model.
You may refer to this web page for more detail.
Depends on what type of project you are working on. If its a really small project, I believe you can use the direct assignment through code. Its the quick & dirty way of doing things. But hey... If it works.. it works. :)
If you are working on a bigger project, I'm sure you will find a lot of advantages of using bindings and the MVVM pattern. Event if you don't know MVVM you will see that using bindings will make you grow into the pattern automatically.
But hey... I believe this is a matter of opinion
You've pretty much covered all the pros and cons of each. With bindings, changes to the text in the code can be automatically updated in the UI and vice versa. As someone else said, if you want to use a view model then the 2nd option will be preferable, but unless you're required to strictly adhere to MVVM principles you can always mix and match as you see fit. That said, it can make your code confusing to yourself and others so it's best to just pick one style and stick with it.