I tried to use InvokeOperation to get a boolean value from the DomainContext in my ViewModel.
As calls to the server are asynchronous, including invokes. I need to wait for the invoke to complete (i.e. ctx.epidemicContext().Completed += [an event handler]). And in invoke complete event I'm retriving the bool value and setting a property.
_context.IsChangeLogStarted(OnInvokeCompleted, null);
//OnInvoke complete set the property
private void OnInvokeCompleted(InvokeOperation<bool> invOp)
{
IsChangeLogExist = invOp.Value;
}
//Property
public bool IsChangeLogExist
{
get { return this._IsChangeLogExist; }
set { this._IsChangeLogExist = value; }
}
And In my View I'm trying to bind the "IsChangeLogExist" to IsEnabled property of Button.
//View
<Button IsEnabled="{Binding IsChangeLogExist}" Command="{Binding Source={StaticResource ProxyViewModel}, Path=StartChangeLogCommand}" CommandParameter="{Binding}" />
But the problem is when I run the application, Button's IsEnabled property is binding with default value (false) of IsChangeLogExist, With out setting its property value from the domaincontext due the Invoke's asynchronous operation.
before invoke callback calls the OnInvokeCompleted, The button's Isenabled property is already getting the value. value is always false (since we are not setting the value and the invoke call is still at server)
Can some one suggest me how to bind IsEnabled property of button after getting the bool value from server and setting it in OnInvokeCompleted method.
You need to implement INotifyPropertyChanged in your view model and then raise this PopertyChanged event in setter of the property "IsChangeLogExists". This will refresh UI and new value of IsChangeLogExist will be used after callback completed.
public bool IsChangeLogExist
{
get { return this._IsChangeLogExist; }
set
{
this._IsChangeLogExist = value;
if(PropertyChanged != null)
PropertyChanged(this, "IsChangeLogExists")
}
}
Related
What's the appropriate way to handle running an async operation when an item is selected from a two-way bound control such as a combobox (wpf data binding)?
When I have a two-way binding property (e.g. SelectedValue on ComboBox) I don't think I can use Stephen Cleary's NotifyTaskCompletion because when a user selects a value from the dropdown, the ComboBox itself would need to modify the bound Result property, which is the Task's result.
The only viable solution I've come up with is calling an async Task -method from the databound setter without awaiting the result. This should be fine as long as the async-method triggers a property changed event for whatever ui-related stuff is being done, and that any exceptions are picked up and propagated to the ui accordingly, right?
I assume this would be a common case in async WPF applications. How do you guys approach this?
My solution so far:
<ComboBox
ItemsSource="{Binding PossibleItems}"
DisplayMemberPath="Name"
SelectedValue="{Binding SelectedItem}"/>
...
public Item SelectedItem
{
get { return m_selectedItem; }
set
{
m_selectedItem = value;
OnPropertyChanged();
InitializeAsyncAndFirePropertyChanged(); // async Task method not awaited - gives compiler warning CS4014
}
}
public async Task InitializeAsyncAndFirePropertyChanged(ObservableCollection<RFEnvironment> possibleRfEnvironments)
{
//should check this method for exceptions and propagate them to the UI via databinding
OtherDataBoundProperty = await GetSomeStringFromWebAsync();
}
public string OtherDataBoundProperty
{
get { return m_otherDataBoundProperty; }
set
{
m_otherDataBoundProperty = value;
OnPropertyChanged();
}
}
Note: I have found similar questions asked, but none that addresses two-way bindings on controls such as a Combobox.
If you are using a functional reactive MVVM framework such as ReactiveUI, you would simply observe the SelectedItem property and kick off any operation you want when the property is set. e.g.:
this.WhenAnyValue(x => x.SelectedItem)
.Subscribe(async _ => await InitializeAsyncAndFirePropertyChanged());
A property itself should ne be kicking off background operations, but a view model may do this when a property is set.
Please refer to the docs for more information: https://reactiveui.net/docs/concepts/.
I have a similar issue with async call in property setter when using WCF databinding.
My solution is slightly better, because in your case when an exception occurs in InitializeAsyncAndFirePropertyChanged, no exception is thrown nor caught. The modified code is below. It uses task continuation to throw an exception and raising OnPropertyChanged. OnPropertyChanged call can stay in an original place, it depends on your needs.
public class MyViewModel: INotifyPropertyChanged
{
private readonly TaskScheduler _uiScheduler;
public MyViewModel()
{
_uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
}
public Item SelectedItem
{
get { return m_selectedItem; }
set
{
m_selectedItem = value;
InitializeAsyncAndFirePropertyChanged()
.ContinueWith(t =>
{
if (t.Exception != null)
{
throw t.Exception;
}
OnPropertyChanged();
}, _uiScheduler);
}
}
public async Task InitializeAsyncAndFirePropertyChanged(ObservableCollection<RFEnvironment> possibleRfEnvironments)
{
//should check this method for exceptions and propagate them to the UI via databinding
OtherDataBoundProperty = await GetSomeStringFromWebAsync();
}
public string OtherDataBoundProperty
{
get { return m_otherDataBoundProperty; }
set
{
m_otherDataBoundProperty = value;
OnPropertyChanged();
}
}
.... other code to support INotifyPropertyChanged
}
I want to set the DataContext of a UserControl to a new value, but I am not being successful at it.
I have an UserControl whose DataContextChanged event would write something to Console.
I have an instance of that UserControl in XAML, with DataContext bound to the correct property;
I have a RaisePropertyChanged (from MVVM-Light) with a breakpoint configured to write to Console too.
The fact is: Even if the RaisePropertyChanged fires (as shown by breakpoint), the DataContextChanged handler is not called (as shown either by breakpoint or Console.WriteLine)
IMPORTANT: When the UserControl is first loaded, it displays the value correctly (that is, it "gets" the value from ViewModel) and the messagem from DataContextChanged is printed to Console. The problem is that I can't get the DataContextChanged handler to fire when calling PontoCanalZoom setter, even if RaisePropertyChanged fires appropriately.
ViewModel:
public class AnáliseViewModel : ObservableObject
{
public PontoAnáliseViewModel PontoCanalZoom
{
get
{
if (_pontoCanalZoom == null)
_pontoCanalZoom = PontosAtivos?.FirstOrDefault();
return _pontoCanalZoom;
}
set
{
_pontoCanalZoom = value;
RaisePropertyChanged(() => PontoCanalZoom);
}
}
private PontoAnáliseViewModel _pontoCanalZoom = null;
}
public ICommand ComandoSelecionarCanalNavegação => new RelayCommand<PontoAnáliseViewModel>(SelecionarCanalNavegação);
private void SelecionarCanalNavegação(PontoAnáliseViewModel ponto)
{
// This calls the setter _and_ raises PropertyChanged event.
PontoCanalZoom = ponto;
}
Instance of Control (AreaPlotagemAnáliseEMG):
<plotagem:AreaPlotagemAnáliseEMG
DataContext="{Binding PontoCanalZoom, Mode=OneWay}"/>
Control definition:
public partial class AreaPlotagemAnáliseEMG : AreaPlotagemBase
{
public AreaPlotagemAnáliseEMG()
{
InitializeComponent();
DataContextChanged += (a, b) => System.Console.WriteLine("Trocou Data Context!!!!!!!!!!!!");
}
}
Just wondering if this is a good practice or if it could cause any troubles in the long run. To be honest, I'm surprised it even works - it does the job, but I'm not sure if it's risky.
Basically we created a NumericTextBox that derives from TextBox, and we overrode the Text property with the new keyword to remove commas from the text:
public class NumericTextBox : TextBox
{
public new string Text
{
get
{
return base.Text.Replace(",", String.Empty);
}
set
{
base.Text = value;
}
}
}
What I don't like about it is that I know Text is a dependency property and we're overriding it, but surprisingly we can still bind to it in XAML:
<this:NumericTextBox x:Name="textBox"
Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=SomeText, Converter={StaticResource debugConverter}}" />
Then in C# when we call textBox.Text we do get the values without commas.
What do you guys think?
Perhaps you should add your class as an owner of the dependency property and override the getter and setter there:
public class NumericTextBox : TextBox
{
public NumericTextBox() { }
public static readonly DependencyProperty NumericTextProperty = TextBox.TextProperty.AddOwner(typeof(NumericTextBox), new PropertyMetadata(null));
public new string Text
{
get { return ((string)this.GetValue(NumericTextProperty )).Replace(",", String.Empty); }
set { this.SetValue(NumericTextProperty , value); }
}
}
You also have the possibility of overriding the metadata of the dependency property to hook in a custom validation callback method.
You approach doesn't work, because WPF doesn't actually use the class properties to change the value, but the dependency property system. It simply calls the SetValue method as you do in your property setter. You can try it out by setting a breakpoint in the setter, and changing the bound property in the gui. The setter breakpoint will never be hit. But you can hook into the events provided by the dependency property metadata.
My problem is simple. I have a treeview bound to an ObservableCollection of objects, and those objects all have their own ObservableCollections. Based on user selection of other criteria on my page I want to dynamically set which checkboxes are checked. Unfortunately my checkboxes fail to update their IsChecked status after I have changed the corresponding bool Property bound to IsChecked. The checkboxes will be in the correct state the first time any node is expanded, but afterwards they stop updating. I suspect this means the objects are not created/evaluated until they are actually due to be shown for the first time.
The structure of data is Silverlight -> ViewModel -> ObservableCollection of StoreGroups LocalStoreGroups -> StoreGroup has ObservableCollection of Store Stores
Through debugging I have noticed that there are no handlers attached to this.PropertyChanged, and am wondering if this is the problem?
Treeview control :
<controls:TreeView ItemsSource="{Binding LocalStoreGroups}" ItemTemplate="{StaticResource TreeviewStoreGroupTemplate}" />
In my project I use a treeview with the following HeirarchalDataTemplates :
<UserControl.Resources>
<sdk:HierarchicalDataTemplate x:Key="TreeviewStoreTemplate">
<CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}" Content="{Binding DTO.Name}" />
</sdk:HierarchicalDataTemplate>
<sdk:HierarchicalDataTemplate x:Key="TreeviewStoreGroupTemplate" ItemsSource="{Binding Stores}" ItemTemplate="{StaticResource TreeviewStoreTemplate}">
<CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}" Content="{Binding DTO.Name}" />
</sdk:HierarchicalDataTemplate>
</UserControl.Resources>
The code for the IsSelected Property (both the StoreGroup object and the Store object have this property :
private bool _IsSelected;
public bool IsSelected
{
get { return _IsSelected; }
set
{
_IsSelected = value;
OnPropertyChanged("IsSelected");
}
}
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler temp = this.PropertyChanged;
if (null != temp)
temp(this, e);
}
Code to change IsSelected
foreach (Store s in LocalStoreGroups.SelectMany(sg => sg.Stores))
{
s.IsSelected = false;
}
foreach (StoreLink link in links)
{
Store targetStore = (from s in LocalStoreGroups.SelectMany(sg => sg.Stores) where s.DTO.ID == link.DTO.StoreID select s).FirstOrDefault();
targetStore.IsSelected = true;
}
It looks like you are updating the property in response to a load event. It is likely then that you are not on the UI thread when you update the property. Unless the change occurs on the UI thread it will not update the display.
For bound properties and properties that are collections (and not children in observable collections) it is only the OnPropertyChanged that needs to be on the UI thread. The properties can change earlier, but the UI will not change bindings until OnPropertyChanged is called.
All our ViewModels derive from a ViewModelBase we created that implements a helper SendPropertyChanged like below (so we never have to worry about cross-threading).
All our notify properties call that instead of calling OnPropertyChanged directly.
It also exposes a generally useful OnUiThread method so you can execute arbitrary code on the UI thread:
protected delegate void OnUiThreadDelegate();
public event PropertyChangedEventHandler PropertyChanged;
public void SendPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.OnUiThread(() => this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)));
}
}
protected void OnUiThread(OnUiThreadDelegate onUiThreadDelegate)
{
if (Deployment.Current.Dispatcher.CheckAccess())
{
onUiThreadDelegate();
}
else
{
Deployment.Current.Dispatcher.BeginInvoke(onUiThreadDelegate);
}
}
Anyways, the give-away here should have been that nobody was subscribed to the PropertyChanged event. Turns out that although I implemented the PropertyChanged event I forgot to actually give the class the INotifyPropertyChanged interface.
I have simple issue setting a two-way databinding of a checkbox in Silverlight 3.0. It must be a no-brainer but probably I forgot my brain home today...
I defined a Model class to represent my .. 'data'. I implemented the INotifyPropertyChanged interface to enable the UI to see when the data changes.
public class Model : INotifyPropertyChanged
{
private bool _value;
public bool Value
{
get { return this._value; }
set
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs("Value"));
this._value = value;
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Next I put a checkbox and a button on the .. 'form' :
<StackPanel Orientation="Horizontal">
<CheckBox x:Name="check" IsChecked="{Binding Value, Mode=TwoWay}" Content="SomeLabel"/>
<Button Click="Button_Click" Content="Test" />
</StackPanel>
Then I supplied the data in the constructor :
public MainPage()
{
InitializeComponent();
this.DataContext = new Model() { Value = true };
}
The issue is that you have to click twice on the checkbox for it to check/uncheck unless I de-implement the INotifyPropertyChanged. If de-implement it however, then the UI doesn't notice if I change the underlying data.
If I remove the Mode=TwoWay bit from the IsChecked binding expression then also the UI won't notice the underlying data change even if the Model is implementing the interface.
How can I do to :
Have the checkbox bound to the data at startup
Have the checkbox IsChecked change to modify the underlying data
Have the checkbox detect the underlying data change and update itself?
You've got a sequencing error in your set property procedure, you need to assign to _value before notifying the change :-
set
{
this._value = value;
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs("Value"));
}