I've created an example to illustrate my problem.
ViewModel:
public class VM : INotifyPropertyChanged
{
private double _value = 1;
public double Value
{
get { return _value; }
set
{
_value = value;
OnPropertyChanged();
}
}
public VM()
{
var timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromTicks(1);
timer.Tick += (s, e) => { Value += 1; };
timer.Start();
}
// OnPropertyChanged stuff ...
}
}
View:
<Window.DataContext>
<namespace:VM/>
</Window.DataContext>
<Grid>
<TextBox Text="{Binding Value, IsAsync=True, FallbackValue=Test}"/>
</Grid>
When running my application the text in the textbox flickers. During the update process the FallbackValue is displayed, which makes no sense to me.
Does anyone knows the purposes or what are the benefits that during the update process the FallbackValue is displayed? Is there a way to display the old Value during an async update process?
This seems normal to me, given that you are using IsAsync=True in your binding. From the documentation:
While waiting for the value to arrive, the binding reports the FallbackValue, if one is available
When the PropertyChanged event is raised, WPF initiates the process of updating the target of the binding. Normally this would happen synchronously, with the property getter called immediately to update the value.
But you are using IsAysnc=True, so instead WPF fills in the target with the fallback value, and starts an asynchronous request to retrieve the actual property value later. Until that request has completed, the fallback value is displayed.
Does anyone knows the purposes or what are the benefits that during the update process the FallbackValue is displayed?
Per the documentation, the intent behind the IsAsync=True setting is that it's used when the property getter is, or could be, slow. Your code has told WPF that the property value has changed, so it knows the old value is no longer valid. Your code has also told (via the IsAsync in the XAML) that the property getter could take some time to provide the new value, so it defers retrieving that value until later.
In the meantime, what should WPF display? That's what the fallback value is there for.
Is there a way to display the old Value during an async update process?
If you don't want the behavior that is designed for this feature in WPF, you should just retrieve the new data asynchronously yourself, and update the property via the setter when you have it. It's not a good idea for a property getter to be slow anyway, so this would be a better design in any case.
I had the same problem but with an image source. I've removed the IsAsync on the binding and I have made my getter async:
// This field hold a copy of the thumbnail.
BitmapImage thumbnail;
// Boolean to avoid loading the image multiple times.
bool loadThumbnailInProgress;
// I'm using object as the type of the property in order to be able to return
// UnsetValue.
public object Thumbnail
{
get {
if (thumbnail != null) return thumbnail;
if (!loadThumbnailInProgress) {
// Using BeginInvoke() allow to load the image asynchronously.
dispatcher.BeginInvoke(new Action(() => {
thumbnail = LoadThumbnail();
RaisePropertyChanged(nameof(Thumbnail));
}));
loadThumbnailInProgress = true;
}
// Returning UnsetValue tells WPF to use the fallback value.
return DependencyProperty.UnsetValue;
}
}
Sometimes a binding will fail, failure is important to consider. Fallback value option presents users a message if an error occurs, rather than nothing happening. If you would like your fallbackvalue to display the previous value contained, I could think of a few ways of trying : possibly saving the value in a reference string and/or to another control, then binding to that control
But if you don't want the fallbackvalue displayed at all, you need to do a code inspection to see how your binding is failing/or is slow, and contain it in your code behind
I've found an approach to avoid flickering by just inheriting from textbox and overriding it's textproperty-metadata.
Custom TextBoxControl
public class CustomTextBox : TextBox
{
static CustomTextBox()
{
TextProperty.OverrideMetadata(typeof(CustomTextBox), new FrameworkPropertyMetadata(null, null, CoerceChanged));
}
private static object CoerceChanged(DependencyObject d, object basevalue)
{
var tb = d as TextBox;
if (basevalue == null)
{
return tb.Text;
}
return basevalue;
}
}
View
<Window.DataContext>
<namespace:VM/>
</Window.DataContext>
<Grid>
<namespace:CustomTextBox Text="{Binding Value, IsAsync=True}"/>
</Grid>
It's important to have the text-binding without a fallbackvalue. So during update process the text is set to the textproperty defalut value - so in this case to null.
The CoerceChanged handler checks whether the new value is null. If it's so he returns the old value so that during update process there is still the old value displayed.
Related
I have an object with an Array of doubles property like this:
private double[] _axes;
public double[] Axes
{
get
{
return _axes;
}
set
{
_axes = value;
if (PropertyChanged != null)
{
NotifyPropertyChanged("Axes[]");
}
}
}
This Array is always assigned as a whole, because i get an Array of double out of my database object and set it to the Axes property like this:
object.Axes = GetDataRow(i);
(this is just to demonstrate that i in fact assign the whole Array)
In my XAML now i bind it like this:
<Label Content="{Binding Path=Axes[0], UpdateSourceTrigger=PropertyChanged}"
I set the DataContext of the window to the Object that has the Axes property and the UI receives one value from it at program start that i assign in the constructor of the object.
However, even though the "Axes[]" event is raised constantly (which i checked from another object), no further values arrive in the UI.
So my question is: CAN this even work? Does Axes[] not correspond to Axes[0]? what could be the problem?
Your property name is incorrect in the notification
if (PropertyChanged != null)
{
NotifyPropertyChanged("Axes");
}
Remove the brackets. This should solve your problem in this case. However, it is recommended to use ObservableCollection for binding a list of objects.
The binding system won't look "into" the array for updates. You just need to notify it that the property itself changed. You're currently saying that a property called Axes[] changed, but you don't have a property with that name. It's just Axes
public double[] Axes
{
get
{
return _axes;
}
set
{
_axes = value;
if (PropertyChanged != null)
{
NotifyPropertyChanged("Axes");
}
}
}
If you really need to know if an individual item in the array is changing, then you should be using ObservableCollection, which the binding system does know how to look "into".
No.
You have no property named Axes[], so that event has no effect.
You should not bind to arrays; instead, use ObservableCollection, and make the property read-only.
My application uses the MVVM architecture, with the ViewModel having no knowledge of the View. When a ViewModel object requires a new View be shown it exposes a public ShowNewView property that is an object whose class is based on my ViewModel base class. The WPF View binds a custom DependencyProperty to this and uses the PropertyChangedCallback to construct and show an approperiate Window.
This all works well the first time the ShowNewView property is set. However, if the user closes the window and then attempts to re-open it, the ShowNewView property's value has not changed when the PropertyChanged event is raised and the PropertyChangedCallback is not invoked.
In order to 'trick' the DependencyProperty into detecting that the value has changed (even though the value stored in the ViewModel's property may not have actually changed), I have used the SetCurrentValue method exposed by the Window class to force the DependencyProperty's value to null.
#region ShowNewViewProperty
private static readonly DependencyProperty _ShowNewViewProperty =
DependencyProperty.RegisterAttached
(
"ShowNewView",
typeof(IRootViewModel),
typeof(WpfViewWindow),
new PropertyMetadata(ShowNewViewPropertyChanged)
);
public static DependencyProperty ShowNewViewProperty { get { return _ShowNewViewProperty; } }
public static IRootViewModel GetShowNewView(Window source)
{
return (IRootViewModel)source.GetValue(ShowNewViewProperty);
}
public static void SetShowNewView(Window target, IRootViewModel value)
{
target.SetValue(ShowNewViewProperty, value);
}
private static void ShowNewViewPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
WpfViewWindow window = d as WpfViewWindow;
IRootViewModel newValue = e.NewValue as IRootViewModel;
if ((null != window) && (null != newValue))
{
// Create a child WpfViewWindow. This method is part of my
// framework that uses ResourceDictionary entries, imported by MEF
// to locate the View class corresponding to the ViewModel parameter's
// class.
WpfViewWindow modelessWindow = window.CreateWpfViewWindow(newValue);
if (null != modelessWindow)
{
// Show the new WpfViewWindow.
modelessWindow.Show();
}
// Clear the current value so that the next PropertyChanged event
// is processed even if the underlying value has not actually changed.
window.SetCurrentValue(ShowNewViewProperty, null);
}
}
#endregion
Technically this works, as it results in the callback being run when the PropertyChanged event fires, regardless of whether the value has actually changed or not. However, it results in the callback being called (recursively) twice every time the ViewModel's property is updated: once in response to the ViewModel's event and once in response to the SetCurrentValue method being called.
There are a number of questions here relating to the PropertyChangedCallback not being called, or not being called more than once, in other situations.
PropertyChangedCallback on DependencyProperty Only Firing Once covers the situation where the property is a collection and the collection content changes, but the collection itself does not. However, my property is not a collection and everything is working exactly as documented.
WPF dependency property setter not firing when PropertyChanged is fired, but source value is not changed looks very promising, but the answer only suggests using the callback that I am already.
Is there a more elegant way to achieve this that does not result in the callback being run twice for each PropertyChanged event from the ViewModel? I.e. is there some way to get around the framework's check to verify that the old and new values are different?
Clarification
The View being created isn't necessarily always a WPF Window, for example, in my unit tests it is a mock, and later in the project it may be a sperate logging assembly. Nor are all of the ViewModel objects from the same assembly, it is known that additional functionality will be required in the future, but the specifics are currently undefined. The application allows the user to connect a device by way of a simple network. Initially the network is ModbusRTU over RS-485, however, the end customer may want to use CANOpen or Profinet or some other transport layer, and I have to provide a plug-in mechanism that allows the new functionality to be added without changing the existing code.
To be fair, there are several alternative mechanisms that I could use to achieve the same result (i.e. having the ViewModel request a new View be created), but I'm interested in knowing if there is a way to make a DependencyPropety 'forget' what it's previous value was.
The usual solution to this type of problem is for you to extract the code from your ShowNewViewPropertyChanged method and to put it into a different method:
private void SomeNewMethod(IRootViewModel newValue)
{
// Create a child WpfViewWindow. This method is part of my
// framework that uses ResourceDictionary entries, imported by MEF
// to locate the View class corresponding to the ViewModel parameter's
// class.
WpfViewWindow modelessWindow = CreateWpfViewWindow(newValue);
if (null != modelessWindow)
{
// Show the new WpfViewWindow.
modelessWindow.Show();
}
// Clear the current value so that the next PropertyChanged event
// is processed even if the underlying value has not actually changed.
SetCurrentValue(ShowNewViewProperty, null);
}
Now you can simply call that method from both the ShowNewViewPropertyChanged handler and from wherever else you desire:
private static void ShowNewViewPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
WpfViewWindow window = d as WpfViewWindow;
IRootViewModel newValue = e.NewValue as IRootViewModel;
if ((null != window) && (null != newValue))
{
window.SomeNewMethod(newValue);
}
}
I've implemented a custom Slider that derives from RangeBase that has an added ResetValue property that can be used to reset the Value to the initial value. Unfortunately this requires double binding the value source and also use the OneTime binding mode for ResetValue.
<ButtonSlider
Minimum="0" Maximum="500" TickFrequency="1"
Value="{Binding Path=MaxVoltageUv}"
ResetValue="{Binding Path=MaxVoltageUv, Mode=OneTime}" />
Ideally it would have a boolean dependency property CanReset that would capture the ResetValue on first bind if it were enabled. I've tried implementing it by capturing the first time Value is set, but for some reason this only works some of the time.
It first overrides the metadata on the ValueProperty:
ValueProperty.OverrideMetadata(typeof(ButtonSlider), new FrameworkPropertyMetadata(OnValueChanged, OnCoerceValue));
And then implements custom coerce and value changed handlers.
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var buttonSlider = ((ButtonSlider)d);
if (buttonSlider != null)
{
if (!buttonSlider.valueInitialized && e.NewValue is double)
{
buttonSlider.resetValue = (double)e.NewValue;
buttonSlider.valueInitialized = true;
}
buttonSlider.CheckResetEnabled();
}
}
private static object OnCoerceValue(DependencyObject d, object baseValue)
{
var buttonSlider = ((ButtonSlider)d);
if (buttonSlider != null)
{
if(baseValue is double)
return Math.Max(Math.Min((double)baseValue, buttonSlider.Maximum), buttonSlider.Minimum);
}
return DependencyProperty.UnsetValue;
}
For some reason the OnValueChanged is called with a Value of 0 while the initial bind should have been 2. This seems to be related to data templating. Controls that are not on a data template work correctly.
Is there any other way to capture an initial value of a dependency property?
My thoughts
Verify that MaxVoltageUv which you bind to is a double.
Remove the dependency property ResetValue it is just creating noise in trying to resolve this.
0 could be the default value of the dependency property which gets set before the actual binding. Why not put in a -1.0 as the default along with the logic in the OnValueChanged to detect that scenario and ignore until a proper value comes through.
If both the data context and the binding are set in XAML, you can use the Loaded event of your custom control to store the current value as the reset value. This will also work if your control is in a data template for instance.
If the data context is set from code, this will only work if it is set before the control has been added to the logical tree.
I am building a small wpf app in C#. When a button gets clicked a third
party dll function constructs a tree like object. This object is bound
to a treeview. This works fine but takes a bit of time to load. As the
dll function constructs the object it prints progress info to the
console. I want to redirect this into a TextBlock so that the user
gets to see the progress messages.
My window ctor looks like this:
InitializeComponent();
StringRedir s = new StringRedir(ref ProgressTextBlock);
Console.SetOut(s);
Console.SetError(s);
this.DataContext = s;
xaml:
<TextBlock Text="{Binding Path=Text}" Width="244"
x:Name="ProgressTextBlock" TextWrapping="Wrap" />
<TreeView >...</TreeView>
The StringRedir class is shown below. The problem is the TextBlock for
some reason does not get updated with the messages until the TreeView
gets loaded. Stepping through I see the Text property being updated
but the TextBlock is not getting refreshed. I added a MessageBox.Show
() at the point where Text gets updated and this seems to cause the
window to refresh each time and I am able to see each message. So I
guess I need some way to explicitly refresh the screen...but this
doesnt make sense I thought the databinding would cause a visual
refresh when the property changed. What am I missing here? How do I
get it to refresh? Any advice is appreciated!
public class StringRedir : StringWriter , INotifyPropertyChanged
{
private string text;
private TextBlock local;
public string Text {
get{ return text;}
set{
text = text + value;
OnPropertyChanged("Text");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
public StringRedir(ref TextBlock t)
{
local = t;
Text = "";
}
public override void WriteLine(string x)
{
Text = x +"\n";
//MessageBox.Show("hello");
}
}
You haven't included the code that is loading the data for the TreeView, but I'm guessing it's being done on the UI thread. If so, this will block any UI updates (including changes to the TextBlock) until it has completed.
So after doing some reading on the WPF threading model ( http://msdn.microsoft.com/en-us/library/ms741870.aspx ) I finally got it to refresh by calling Dispatcher Invoke() with Dispatch priority set to Render. As Kent suggested above UI updates in the dispatcher queue were probably low priority. I ended up doing something like this.
XAML
<TextBox VerticalScrollBarVisibility="Auto"
Text="{Binding Path=Text, NotifyOnTargetUpdated=True}"
x:Name="test" TextWrapping="Wrap" AcceptsReturn="True"
TargetUpdated="test_TargetUpdated"/>
C# target updated handler code
private void test_TargetUpdated(object sender, DataTransferEventArgs e)
{
TextBox t = sender as TextBox;
t.ScrollToEnd();
t.Dispatcher.Invoke(new EmptyDelegate(() => { }), System.Windows.Threading.DispatcherPriority.Render);
}
Note: Earlier I was using a TextBlock but I changed to a TextBox as it comes with scrolling
I still feel uneasy about the whole flow though. Is there a better way to do this?
Thanks to Matt and Kent for their comments. If I had points would mark their answers as helpful.
I believe the problem is in the constructor of your StringRedir class. You're passing in ProgessTextBlock, and you're doing this to it:
local.Text = "";
This is effectively overwriting the previously set value for ProgressTextBlock.Text, which was this:
{Binding Text}
See what I mean? By explicitly setting a value to the TextBlock's Text property, you've cancelled the binding.
If I'm reading right, it looks like the idea of passing a TextBlock into the StringRedir's ctor is a hangover from before you tried binding directly. I'd ditch that and stick with the binding idea as it's more in the "spirit" of WPF.
I have a grid that is binded to a collection. For some reason that I do not know, now when I do some action in the grid, the grid doesn't update.
Situation : When I click a button in the grid, it increase a value that is in the same line. When I click, I can debug and see the value increment but the value doesn't change in the grid. BUT when I click the button, minimize and restore the windows, the value are updated... what do I have to do to have the value updated like it was before?
UPDATE
This is NOT SOLVED but I accepted the best answer around here.
It's not solved because it works as usuall when the data is from the database but not from the cache. Objects are serialized and threw the process the event are lost. This is why I build them back and it works for what I know because I can interact with them BUT it seem that it doesn't work for the update of the grid for an unkown reason.
In order for the binding to be bidirectional, from control to datasource and from datasource to control the datasource must implement property changing notification events, in one of the 2 possible ways:
Implement the INotifyPropertyChanged interface, and raise the event when the properties change :
public string Name
{
get
{
return this._Name;
}
set
{
if (value != this._Name)
{
this._Name= value;
NotifyPropertyChanged("Name");
}
}
}
Inplement a changed event for every property that must notify the controls when it changes. The event name must be in the form PropertyNameChanged :
public event EventHandler NameChanged;
public string Name
{
get
{
return this._Name;
}
set
{
if (value != this._Name)
{
this._Name= value;
if (NameChanged != null) NameChanged(this, EventArgs.Empty);
}
}
}
*as a note your property values are the correct ones after window maximize, because the control rereads the values from the datasource.
It sounds like you need to call DataBind in your update code.
I am using the BindingSource object between my Collection and my Grid. Usually I do not have to call anything.