I have a multi-language silverlight application where the resources are stored in resx files for different languages and bound to the buttons' and labels' Content Properties inside the xaml.
When setting the thread's UI culture in the constructor of the silverlight's page every thing works fine, but when changing it based on user's selection (through combobox selection)
the interface doesn't change. I need the page to redraw the controls and rebind to the resource files based on the new thread's UI culture.
I am not sure what mechanism you are using to bind your view to your localization repository, but I am guessing that the problem lies with notification.
The view will only update the data in a binding when it gets a notification event. Most likely, the object with the localization data that you are binding to is not sending notifications when the culture changes.
You might consider adding an INotifyPropertyChanged to the object that holds your localization strings. Then, add a method to that class called "NotifyThatEverythingChanged". In that method, just send that the property string.Empty has changed, which tells the UI to update everything in the data context.
In my case, I have the object that the RESX auto-generated for me called MyUIStrings. It has a bunch of static strings in it. I derive from that class, and add the functionality to notify that everything has changed. The UI will act accordingly:
public class NotifyableUIStrings : MyUIStrings, INotifyPropertyChanged
{
public void NotifyThatEverythingChanged()
{
OnPropertyChanged(string.Empty);
}
protected void OnPropertyChanged(string propertyName)
{
var handlers = PropertyChanged;
if(handlers != null)
handlers(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Related
I'm a web and backend programmer by nature. Normally I try to avaoid making windows programs. Now I have to make a WPF client.
I have a background task that raises an event every often time. (It is working like a poller and when the criteria are met an event is raised). Noob as I am I wrote this code that was attached to the event to update the UI.
private void IsDisconnectedEvent()
{
UserWindow.Visibility = Visibility.Hidden;
DisconnectWindow.Visibility = Visibility.Visible;
}
This gives an exception because I am not on the same thread. After some googling I found that I should change the code with:
private void IsDisconnectedEvent()
{
Dispatcher.Invoke(() =>
{
UserWindow.Visibility = Visibility.Hidden;
DisconnectWindow.Visibility = Visibility.Visible;
});
}
This works, but this is not the only event and thus makes my code horrible ugly. Are there better ways to do this?
Regarding this:
This works, but this is not the only event and thus makes my code
horrible ugly
Yes, your WPF-based code will definitely be extremely horrible unless you understand and embrace The WPF Mentality.
Basically, all interactions between your custom logic (AKA Business logic or Application Logic) and the WPF UI should manifest in the form of Declarative DataBinding as opposed to the traditional imperative approach.
This means that there should be nothing like this:
UserWindow.Visibility = Visibility.Hidden;
anywhere in your code, simply because introducing things like that makes your code dependent on the UI and thus only executable on the UI thread.
Instead, the WPF approach to that would be to declaratively DataBind the Visibility propety of the UI element (IN XAML) to a relevant bool property that you can operate from the outside, like this:
<UserWindow Visibility="{Binding ShowUserWindow, Converter={my:BoolToVisibilityConverter}}">
<!-- ... -->
</UserWindow>
Then, you would need to create a relevant class that contains the properties the UI is expecting to bind to. This is called a ViewModel.
Notice that in order to properly support Two-Way WPF DataBinding, your ViewModels must Implement the INotifyPropertyChanged interface.
When doing so, it is also convenient to have the PropertyChanged event from that interface marshalled to the UI thread, so that you no longer have to worry about setting the ViewModel's properties by using the Dispatcher.
Therefore our first step is to have all our ViewModels inherit from a class like this:
(taken from this answer):
public class PropertyChangedBase:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
//Raise the PropertyChanged event on the UI Thread, with the relevant propertyName parameter:
Application.Current.Dispatcher.BeginInvoke((Action) (() =>
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}));
}
}
Once we have our Property Change Notification Dispatch to the UI Thread in place, we can proceed to create a relevant ViewModel that suits, in this case, the UserWindow and it's DataBinding expectations:
public class UserViewModel: PropertyChangedBase
{
private bool _showUserWindow;
public bool ShowUserWindow
{
get {return _showUserWindow; }
set
{
_showUserWindow = value;
OnPropertyChanged("ShowUserWindow"); //This is important!!!
}
}
}
Finally, you would need to set the Window's DataContext to an instance of it's corresponding ViewModel. One simple way to do that is in the Window's constructor:
public UserWindow() //Window's Constructor
{
InitializeComponent(); //this is required.
DataContext = new UserViewModel(); //here we set the DataContext
}
As you can see in this example, there is literally no need to manipulate the UI element's properties in procedural code. This is good not only because it resolves the Thread Affinity issues (because now you can set the ShowUserWindow property from any thread), but also because it makes your ViewModels and logic completely decoupled from the UI and thus testable and more scalable.
This same concept applies to EVERYTHING in WPF.
One detail that I need to mention is that I'm making use of a technique of Combining MarkupExtension and IValueConverter in order to reduce the the XAML boilerplate involved in using Converters.
You can read more about that in the link and also the MSDN DataBinding page linked above.
Let me know if you need further details.
Editing this entire post to clarify... I cannot seem to nail this:
BackgroundWorker receives data from a WCF service that is a list of objects. The service reference is configured to be ObservableCollection.
I pass the ObservableCollection via a delegate into my main UI thread and set it equal to the UI threads Local Collection.
A listbox is bound to this local collection and does not update. I've added the following to my collection:
public ObservableCollection<EmployeeData> _empData { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<EmployeeData> EmpData
{
get { return _empData ; }
set
{
_empData = value;
OnPropertyChanged("EmpData");
}
}
private void OnPropertyChanged(string p)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(p));
}
This even fires but the PropertyChanged is always null. My XAML listbox has a binding declared as:
ItemsSource="{Binding Path=EmpData}"
No matter what I do EmpData updates but the ListBox does not, I've tried several other methods but nothing ever changes in the listbox, its always just null.
I've been working on this for over a day now, I cannot seem to get this whole automatic updating thing to 'click'.
I'm not sure that I understand exactly what you are doing, but here are a couple of suggestions.
Have a single ObservableCollection
Bind your itemcollection (or listbox, or whatever) to this
Depending on the user, clear and fill that observablecollection with list data
Have the background worker update the list and refresh the observable collection if anything has changed.
Ideally your EmployeeData class will implement the INotifyPropertyChanged interface, so that property changes will get automatically updated in your view.
I have a WPF application, and the design follows the standard MVVM model.
When the underlying data changes, my view model fires the PropertyChanged event so that the controls can update.
My main view is a tab control, so the majority of the controls are invisible at any one time. There are performance problems, and I've realised that much of the CPU time is dedicated to fetching data to update invisible controls. (My view model uses lazy evaluation, so it fires the PropertyChanged events, but doesn't actually calculate the final displayable properties until asked).
Does WPF have a standard way to deal with this problem?
Idealy, if an invisible control receives a relevant PropertyChanged event, it should just think "I must requery that property once I'm visible again".
I don't think there is any infrastructure to handle deactivating bindings associated with non-visible controls. Unfortunately there are many situations in which you would want a control that is not visible to participate in databinding. Most importantly, you often have a control whose visibility itself depends on a binding. Also, you might have a binding between properties of a visible control and a non-visible control. Or someone might want the exact opposite of what you want: the control to populate itself while non-visible and then jump out fully populated once visible.
I think the only good solution for your situation is to avoid having heavyweight non-visible controls, if that is possible. Specifically for your tab control, I would have thought that would be the default behavior, but perhaps it depends on your situation. Ironically some people complain that the TabControl destroys its children when switching between tabs and would like to know how to prevent that because keeping all the background tabs in memory takes some work. But you seem to have the opposite problem.
For reference, here is the source I mentioned for TabControl children:
Keeping the WPF Tab Control from destroying its children
You might be able to do some experiments in a small project to "turn on" the recycling behavior they are trying to turn off. If your control were loaded on demand then tab switching might be little slower but the performance on a tab would improve.
We did something along these lines in our base ViewModel..
Note, You have to freeze/thaw corresponding with the View's visibility.
It basically traps all the PropertyChanged events while frozen, and pushes them out when thawed. While also not keeping dupes, as they don't matter in our case.
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private readonly HashSet<string> hashSet = new HashSet<string>();
private bool isFrozen;
protected void RaisePropertyChanged(string propertyName)
{
if (isFrozen)
{
lock (hashSet)
{
hashSet.Add(propertyName);
return;
}
}
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
protected void Freeze()
{
isFrozen = true;
}
/// <summary>
/// Enable PropertyChanged Events to fire again
/// </summary>
protected void Thaw(bool fireQueued)
{
isFrozen = false;
if (fireQueued)
{
lock (hashSet)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
foreach (string propertyName in hashSet)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
hashSet.Clear();
}
}
else
{
hashSet.Clear();
}
}
}
My base view model has a IsVisible property. When the view model is invisible just suppress property changed notifications. When it becomes visible fire off a property changed event for each property (pr pass in null to the property name)
I set up two Windows in my WPF C# Blend project. Now what I would like to know (and I have trouble finding some clear documentation) is how to run both windows on application startup and in what way I can pass code from window to window.
Thanks
In the app.xaml file for the solution, it specifies which window to run upon startup. A quick solution to open the other one is to tack on an event handler to the startup window's Loaded event which opens the second window.
However, that's not too scalable of a solution if this is part of a larger project. Having a separate class which can open each window, then neither window needs to know about the other.
As for passing data between them, using events can offer a more loosely-coupled solution. I'd push for a more MVVM (Model-View-ViewModel) architecture, then let each of the ViewModels raise events that the other can respond to. You can declare your own subclass of EventArgs which would supply the information which needs to be passed.
Update
Sorry for the delay in response. Simply, to have one Window share data with another Window, the receiver must have a way to receive that data. Defining a public property in the receiver will allow the sender to specify the data with a simple property call. By default, a Window's controls are internal, so you can access them within the same assembly, but that's not the best way to do it.
WPF has a really rich binding infrastructure that you should be taking advantage of. Do do this, your object which is providing data to the Window needs to implement the INotifyPropertyChanged interface. This will alert the UI that data has changed, and the binding should update the target with the changed data. The MSDN page describing the interface as well as a sample implementation can be found here.
When you implement that interface, that will expose an event (PropertyChanged) which will fire when data has been changed. The object providing data to the other window can register an event handler to listen for these changes, and then it will have the updated value.
Here's an example implementation of a simple class with a FirstName and LastName property.
class FirstNameViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string firstName;
public string FirstName
{
get { return firstName; }
set
{
if(firstName == value)
return;
firstName = value;
if(PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("FirstName"));
}
}
private string lastName;
public string LastName
{
get { return lastName; }
set
{
if(lastName == value)
return;
lastName = value;
if(PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("LastName"));
}
}
}
You can see that there's some code duplication in here--that's often refactored into a base ViewModel class. You'll see that this exposes the PropertyChanged event. Attach an event handler to it, and in the PropertyChangedEventArgs object the handler receives, the PropertyName property will contain the name of the property that was changed (the same as the string passed to the constructor in each of the setters above). The sender parameter will be a reference to the object itself. Cast it to the correct type, and you'll have access to the properties.
Hopefully that gets you a start. I wrote a very contrived sample that I can upload somewhere if you'd like to see it. It opens 2 windows, then you can see that typing in one window causes the typed text to appear in the other one, and vice versa.
If you want to pass data between the windows you should have pointers to the other window in each window. Either that, or you could read up on using a singleton-class. This is handy if you want the windows to share settings, more than sending a lot of data between them.
http://www.yoda.arachsys.com/csharp/singleton.html
Do folks have any guidance on when a simple .NET property that fires INotifyPropertyChanged.PropertyChanged is sufficient in a view model? Then when do you want to move up to a full blown dependency property? Or are the DPs intended primarily for views?
There are a few approaches:
1. The dependency property
While you using the dependency property it makes the most sense in elements-classes that have a visual appearance (UIElements).
Pros:
WPF do the logic stuff for you
Some mechanism like animation use only dependency property
'Fits' ViewModel style
Cons:
You need to derive form DependencyObject
A bit awkward for simple stuff
Sample:
public static class StoryBoardHelper
{
public static DependencyObject GetTarget(Timeline timeline)
{
if (timeline == null)
throw new ArgumentNullException("timeline");
return timeline.GetValue(TargetProperty) as DependencyObject;
}
public static void SetTarget(Timeline timeline, DependencyObject value)
{
if (timeline == null)
throw new ArgumentNullException("timeline");
timeline.SetValue(TargetProperty, value);
}
public static readonly DependencyProperty TargetProperty =
DependencyProperty.RegisterAttached(
"Target",
typeof(DependencyObject),
typeof(Timeline),
new PropertyMetadata(null, OnTargetPropertyChanged));
private static void OnTargetPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Storyboard.SetTarget(d as Timeline, e.NewValue as DependencyObject);
}
}
2. The System.ComponentModel.INotifyPropertyChanged
Usually, when creating a data object, you’ll use this approach. It is simple and neat solution for Data-like stuff.
Pros and Cons - complementary to 1. You need to to implement only one event (PropertyChanged).
Sample:
public class Student : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
PropertyChanged(this, e);
}
}
private string name;
public string Name;
{
get { return name; }
set {
name = value;
OnPropertyChanged(new PropertyChangedEventArgs("Name"));
}
}
3.PropertyNameChanged
Rising an event for each property with specified name(f.e. NameChanged). Event must have this name and it is up to you to handle/rise them. Similar approach as 2.
4. Get the binding
Using the FrameworkElement.GetBindingExpression() you can get the BindingExpression object
and call BindingExpression.UpdateTarget() to refresh.
First and second are the most likely depending what is your goal.
All in all, it is Visual vs Data.
As far as I know, DependencyProperty is only required when you need
PropertyValue inheritence
you need to allow the property to be set in Style setters
Use animation for the property
etc.
These features will not be available with normal properties.
DependencyProperty is required if you want to allow a binding to be set on the property. Usually this is for custom UIElements you create. You want to allow people to be able to bind data to your UIElements.
<local:MyUIElement MyProperty={Binding Path=SomethingToBindTo} />
To do this requires that MyProperty is a dependancy property
The main problem I see with INotifyPropertyChanged is if you viewmodel is complex containing many nested types it appears that you have to bubble the PropertyChanged event up through the hierarchy.
As the other answers have already said enough about when to create dependency property. i.e.
PropertyValue inheritence
you need to use binding on a property
Use animation for the property
The one more perspective/question on this is "In a WPF application is makes sense to create dependency properties in a control cause they are likely to change during user interaction like Height,width, text,content, background etc but what about other classes like Behaviors Classes(Non UI classes). Do properties in those classes need to be a dependency property?"
I won't say for very absolute or emphasis on some set of rules here but you should create your properties as DP. As from design perspective if a property is DP it's always in default form of WPF to use/bind.i.e.
As a DP is much more fast/natural in reflecting changes compare to a normal CLR property.
A DP has validation mechanism to validate the value assigned and a default structure to revert the value.
A DP has Coerce value callback to control the limits of property.
A DP has meta data associated with it unlike CLR property.
In terms of practices I've seen people doing many mistakes in nested bindings and then raising changes these kind of faults doesn't happen with a DP cause of it's design and compatibility of raising change itself. So with a little extra syntax you put a flexibility/performance/ easeness to your application. So go for it wherever affordable.
Still can't say sure for ViewModel classes/other helper classes. will update the answer if found convincing reasons in future.
Just a post worth reading on this topic