WPF View not rendering on changing contentControl bound property - wpf

I am changing user controls on my main window using a bound property on a content control.
XMAL:
<ContentControl Grid.Row="0" Content="{Binding MainContent, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"></ContentControl>
PROPERTY:
private UserControl _mainContent;
public UserControl MainContent
{
get
{
return _mainContent;
}
set
{
_mainContent = value;
OnPropertyChanged();
}
}
CODE BEHIND:
MainContent = new TestUserControl();
ON PROPERTY CHANGED:
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
protected virtual void OnPropertyChanged()
{
string propertyName = new System.Diagnostics.StackTrace().GetFrame(1).GetMethod().Name.Substring(4);
OnPropertyChanged(propertyName);
}
My Problem is that on one specific test machine (similar to others, nothing unique or strange about it. Windows 7, 4GB Ram etc), when changing the usercontrols using this mechanism, the application hangs.
Looking at my logs, the Change command is received, the new user control is instantiated, the constructor runs. The Main Content Property is set, the OnPropertyChanged event fires
and then nothing. The application hangs and windows says its not responding and closes the app.
The OnLoaded event of the user control never gets fired.
This happens on loading any user control this way on the specific machine.
Ideas, comments are all welcome. Idea how to debug this one are welcome.
UPDATE:
As this is a test machine, its not rebooted very often.
Once we rebooted the machine, the problem went away. I would still like to know why and how to stop this happening again.
PS. The Target platform is x86 and the problem machine is x64, but on the other window 7 x64 there was no issue. We are using .net framework 4.0

I ran into this once before, it was caused by me using:
protected virtual void OnPropertyChanged()
{
string propertyName = new System.Diagnostics.StackTrace().GetFrame(1).GetMethod().Name.Substring(4);
OnPropertyChanged(propertyName);
}
Your properties get inlined in release mode (and perhaps during your compile) so the property name was not correctly identified by the stack search...

Related

WPF Is there a simple way to update GUI from main thread [duplicate]

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.

Data Binding Value not updating from other thread

I have a WPF Application where I have the following StepCount Property in my ViewModel, which implements INotifyPropertyChanged, and then I have it bound to a TextBox in my View.
public int StepCount
{
get { return _stepCount; }
set
{
_stepCount = value;
OnPropertyChanged("StepCount");
}
}
In the XAML, here is what the DataBinding looks like:
<TextBox Text="{Binding Path=StepCount}" />
This works great, and if I change the StepCount value, the Textbox value updates accordingly.
However, my issue is that I have another thread that is incrementing the StepCount, and in that case, the TextBox value is not updating. As soon as the thread ends, the Textbox value updates to the correct value.
I need the Textbox value to update everytime my other thread increments the StepCount. As it is right now, the Textbox value only shows an update after the thread finishes.
The other thread is incrementing StepCount, but the change is not being displayed in the UI until the thread ends.
Any ideas?
UPDATE
I appreciate all of the responses. This issue was puzzling because code that was previously working seemed to quit working, as was the case with these particular bindings.
When I installed VS 2011 Beta, it installs the .NET 4.5 Beta Framework, and when I uninstalled VS 2011 Beta under the suspicion that it may be causing problems, it did not uninstall the .NET 4.5 Beta Framework.
I just now uninstalled the .NET 4.5 framework and did a repair install of the .NET 4.0 framework. After completing those steps, my data bindings worked correctly, and now Textbox is correctly updating whenever another thread increments StepCount.
So, it appears, the .NET 4.5 Beta Framework may cause issues with data bindings.
I'll follow up with this by submitting an issue with Microsoft.
Thanks everyone for your responses.
As you've discovered the WPF classes that you've bound your View to in the View model operate within the UI thread.
Ideally you should change the StepCount property of your view model using the WPF Dispatcher and the Invoke command. This will marshell the call appropriately.
See this article for more information on this process.
Tested just now, and all it works ok
public partial class MainWindow : Window
{
SimpleClass simpleClass = new SimpleClass(10);
public MainWindow()
{
InitializeComponent();
DataContext = simpleClass;
}
void IncrementViewModelProperty()
{
simpleClass.StepCount += 11;
Thread.Sleep(5000);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//works the same
//Thread thread = new Thread(() => IncrementViewModelProperty());
//thread.Start();
Action act = IncrementViewModelProperty;
act.BeginInvoke(null, null);
}
}
public class SimpleClass : INotifyPropertyChanged
{
public SimpleClass(int stepCnt)
{
StepCount = stepCnt;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public int StepCount
{
get { return _stepCount; }
set
{
_stepCount = value;
NotifyPropertyChanged("StepCount");
}
}
private int _stepCount;
}
then in xaml:
<StackPanel>
<TextBlock Text="{Binding Path=StepCount, Mode=OneWay}" />
<Button Content="Increment value in separate thread" Click="Button_Click" />
</StackPanel>
BUT! if you will click the button fast and often, you exhausted thread pool and it is really would not update the property as expected.

Databound controls shouldn't update if they're not visible

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)

Sharing DataContext to UserControl in a type safe way

I'm developing my first Silverlight 4 app and are struggling on how to to share my DataContext set on the top element (a Grid) in my MainPage.xaml into an underlying UserControl, in a type safe way. The DataContext is an instance of my ViewModel class and my thought is to be able to bind certain elements in the UserControl to properties of the ViewModel.
I am pretty sure the ViewModel object bubbles down to my UserControl but how can I in the UserControl asure that the DataContext is of type PatternCreatorViewModel?
Hope this was understandable!
This is (in my lonely opinion) one of the biggest limitations of the data binding model in Silverlight and WPF, namely, that there's no type safety anywhere in the process. As soon as you type {Binding...} you're working without a net. MS managed to take a wonderfully glorious strongly-typed language like C# and tied it to a completely non-type-safe data binding model, thereby all but wrecking a decade of Anders Hejlsberg's wonderful work on C#. You expect this sort of "looseness" when working with dynamic languages, but not when you're dealing with C#.
This limitation really becomes problematic when you're changing the ViewModel underlying your Views, because of course, there's no easy way to test your data bindings. Normally, when you've got code that you can't test, you can at least rely on the compiler to tell you if what you're asking the code to do doesn't make any sense. But because MS made data bindings non-type-safe, not only can you not test your changes, you can't even rely on the compiler to tell you when they don't make any sense. And, to add insult to injury, you can't even rely on running your application and seeing if you get any error messages: because bindings always fail silently. The best you can do is turn up the logging level and walk through tons of debug error messages. Uggh. Nasty as hell.
See my blog posting here, another question I asked here, and my answer here for more thoughts on the underlying issue.
I should note that I seem to be virtually alone in my opinion about this one, so perhaps there's something huge that I'm just missing. But I personally think you've hit the nail right on the head.
Pleasd see update below: the first proposed solution may cause threading-issues.
One possibility is creating a DependencyProperty of the required type, updating that during DataContextChanged and binding to that.
DefaultEditor.xaml.cs:
public partial class DefaultEditor : UserControl
{
public DefaultEditor()
{
this.DataContextChanged += OnDataContextChanged;
InitializeComponent();
}
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
SetValue(propertyNameProperty, this.DataContext as IPropertyProvider);
}
public static readonly DependencyProperty propertyNameProperty = DependencyProperty.Register(
nameof(PropertyProvider), typeof(IPropertyProvider), typeof(DefaultEditor), new PropertyMetadata(default(IPropertyProvider)));
public IPropertyProvider PropertyProvider
{
get { return (IPropertyProvider)GetValue(propertyNameProperty); }
}
}
Note: I reduced the standard Visual Studio pattern for the DepenendencyProperty, because I did not want a public setter.
Also, make sure to add the event handler before calling InitializeComponent().
DefaultEditor.xaml:
<UserControl x:Class="MyApp.DefaultEditor" x:Name="self">
<Grid DataContext="{Binding ElementName=self, Path=PropertyProvider}">
<StackPanel>
<TextBlock Text="{Binding Index}"/>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</Grid>
</UserControl>
Once x:Name="self" is defined I have Intellisense for Path=PropertyProvider and the other bindings.
Note: Be sure not to set the entire control's DataContext (which would be recursive), use another (top-level) element like the Grid.
And, in case it wasn't obvious: In the example above IPropertyProvider is a custom type that must be replaced with the required type.
UPDATE:
While the above does work, it can invite code to access DefaultEditor.PropertyProvider from a wrong thread (other than the element's dispatcher's thread), which leads to an InvalidOperationException. This can be resolved by replacing the DependencyProperty with a simple property and implementing INotifyPropertyChanged.
Here's an updated code-behind, the .xaml remains the same.
DefaultEditor.xaml.cs
public partial class DefaultEditor : UserControl, INotifyPropertyChanged
{
public DefaultEditor()
{
this.DataContextChanged += OnDataContextChanged;
InitializeComponent();
}
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
PropertyProvider = this.DataContext as IPropertyProvider;
}
private IPropertyProvider _propertyProvider;
public IPropertyProvider PropertyProvider { get => _propertyProvider; private set => SetField(ref _propertyProvider, value); }
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
#endregion
}
Note: The INotifyPropertyChanged implementation is a Visual Studio (Resharper?) code-snippet.
Using this code, DefaultEditor.PropertyProvider can be accessed from any thread.

wpf: update multiple controls via dispatcher

I'm reading data from a serial port using an event listener from the SerialPort class. In my event handler, I need to update many (30-40) controls in my window with xml data coming over the serial port.
I know that I must use myControl.Dispatcher.Invoke() to update it since it's on a different thread, but is there a way to update lots of controls together, rather than doing a separate Invoke call for each (i.e. myCon1.Dispatcher.Invoke(), myCon2.Dispatcher.Invoke(), etc)?
I'm looking for something like calling Invoke on the container, and updating each child control individually, but I can't seem to work out how to accomplish this.
Thanks!
What you need to do is use MVVM.
You bind your controls to public properties on a ViewModel. Your VM can listen to the serial port, parse out the xml data, update its public properties, and then use INotifyPropertyChanged to tell the UI to update its bindings.
I'd suggest this route as you can batch notifications and, if you have to, use the Dispatcher to invoke the event on the UI thread.
UI:
<Window ...>
<Window.DataContext>
<me:SerialWindowViewModel />
</Window.DataContext>
<Grid>
<TextBlock Text="{Binding LatestXml}/>
</Grid>
</Window>
SerialWindowViewModel:
public class SerialWindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string LatestXml {get;set;}
private SerialWatcher _serialWatcher;
public SerialWindowViewModel()
{
_serialWatcher = new SerialWatcher();
_serialWatcher.Incoming += IncomingData;
}
private void IncomingData(object sender, DataEventArgs args)
{
LatestXml = args.Data;
OnPropertyChanged(new PropertyChangedEventArgs("LatestXml"));
}
OnPropertyChanged(PropertyChangedEventArgs args)
{
// tired of writing code; make this threadsafe and
// ensure it fires on the UI thread or that it doesn't matter
PropertyChanged(this, args);
}
}
And, if that isn't acceptable to you (and you want to program WPF like its a winforms app) you can use Dispatcher.CurrentDispatcher to Invoke once while you manually update all controls on your form. But that method stinks.

Resources