How to know when a command has been invoked on UserControl? - wpf

I need to do some visual effects on the control after a particular ICommand was executed. For example, my Custom control exposes AAACommand and BBBCommand properties.
<myControl AAACommand={Binding ACommand}
BBBCommand={Binding BCommand} />
where ACommand and BCommand are Commands on ViewModel. How do I know when AAACommand was executed, so I can do some UI stuff in my UserControl? there is no Executed event for ICommand to subscribe to.
Edit: AAACommand is defined like this on my user control:
public static readonly DependencyProperty AAACommandProperty =
DependencyProperty.Register("AddCommand", typeof(RelayCommand), typeof(MyCustomControl), null);
public static readonly DependencyProperty AAACommandParameterProperty =
DependencyProperty.Register("AAACommandParameter", typeof(object), typeof(MyCustomControl), null);
public RelayCommand AAACommand
{
get { return (RelayCommand)GetValue(AAACommandProperty); }
set { SetValue(AAACommandProperty, value); }
}
public object AAACommandParameter
{
get { return (object)GetValue(AAACommandParameterProperty); }
set { SetValue(AAACommandParameterProperty, value); }
}
So, there is no problem in invoking ACommand on ViewModel, this works without problem. The problem is how will my user control know when AAACommand will execute ACommand, so it can do something with its UI.

You want to update the usercontrol based upon a response returned from the view model and the command executed? I asked a question similar in nature where I wanted to pass as string value from one user control to another user control. I accomplished this using INotifyProperty Changed event. You can read the original question and solution here
Update to comment:
Base on your comment it seems like one of two things could happen. If you don't need the VM to respond then the update could be triggered by elements in the view. You could do this using Binding ElementNameProperty. This in essence allows you to trigger/change a property based upon the action of another element. (typing text in one field displays the value in another control) Here is the msdn description and example .
If you need it to be invoked based upon the return (i.e. success or failure) then the ViewModel will need to have a property (like a bool) that is bound two-way to the property of the element in the ui.
You may need to create a converter (inheriting IValueConverter ) to handle the binding but INotifyProp Change would be used to marshal the update between the controls or the bound elements within them.
Here is a quick example:
within my xaml I added a user control that I did not want to be visible within the UI until another button within a secondary usercontrol was clicked. To handle this I setup binding on the Visibility property
<ctrl:LandingPage x:Name="ucLandingPage"
Grid.Row="1"
DataContext="{Binding}"
Visibility="{Binding LandingPageVisibility, Mode=OneWay, Converter={StaticResource LandingPageVisibilityConverter}}"/>
Within the viewmodel I had the following property and code
// Default ctor
public SearchViewModel()
{
//Show that the Landing Page control is being displayed
SearchVisibility = Visibility.Collapsed;
}
property in VM (note I use SimpleMVVM framework which has Inotify included in the base object so my notify prop event may look a bit different from yours)
private Visibility _SearchVisibility;
public Visibility SearchVisibility
{
get { return _SearchVisibility; }
set
{
_SearchVisibility = value;
NotifyPropertyChanged(m => m.SearchVisibility);
}
}
Then the method within the VM that updated this property
public void GetSearchResult()
{
currentPage = 1;
//Set the visibility of the search control in the center of the page
SearchVisibility = Visibility.Visible;
this.SearchHistory = this._DataModel.AddSearchHistoryItem(this.SearchTerm);
}
And finally the converter class which would convert the return value to the correct property value for the element
public class SearchVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null & System.Convert.ToString(value) == "Visible")
{
return Visibility.Visible;
}
else
{
return Visibility.Collapsed;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Related

Silverlight UI not updating - ObservableCollection being reinstantiated

I'm pretty new to ObservableCollections, but have built some code which I'm sure should work. Unfortunately it doesn't. The only thing that is not happening, is my GUI is not being updated. I know the values are being updated in the back (Checked using Debugger).
What am I doing wrong?
Here with a sample of my XAML for the Textblock:
<TextBlock Name="tbCallsOpen" Text="{Binding IndicatorValue}" />
Herewith sample of my code behind:
public partial class CurrentCalls : UserControl
{
Microsoft.SharePoint.Client.ListItemCollection spListItems;
ObservableCollection<CurrentCallIndicator> CallIndicators = new ObservableCollection<CurrentCallIndicator>();
public CurrentCalls()
{
InitializeComponent();
DispatcherTimer dispatchTimer = new DispatcherTimer();
dispatchTimer.Interval = new TimeSpan(0, 0, 20);
dispatchTimer.Tick += new EventHandler(BindData);
dispatchTimer.Start();
}
private void BindData(object sender, EventArgs args)
{
//splistitems is a sharepoint list. Data is being retrieved succesfully, no issues here.
foreach (var item in spListItems)
{
//My custom class which implements INotifyPropertyChanged
CurrentCallIndicator indicator = new CurrentCallIndicator();
indicator.IndicatorValue = item["MyValueColumn"];
//Adding to ObservableCollection
CallIndicators.Add(indicator);
}
//Setting Datacontext of a normal TextBlock
tbCallsOpen.DataContext = CallIndicators.First(z => z.IndicatorName == "somevalue");
}
}
You are most likely assuming that changes to the underlying items in the collection will raise the CollectionChanged event; however that is not how the ObservableCollection<T> works.
If you wanted this behavior you would need to roll your own implmentation and when a PropertyChanged event is fired within an item within your collection, you would then need to fire the CollectionChanged event.
Your code looks more-or-less correct to me, at first blush - though I wouldn't expect that you'd need to use an ObservableCollection<> to get the results you seem to be expecting: a simple List<> would work just fine.
If the debugger tells you that the DataContext is being updated correctly to the expected item, then the most likely issue is that there's a problem with how your binding is defined. If you're not seeing any binding errors reported in your debug window, then I'd look into Bea Stollnitz' article on debugging bindings. Most specifically, I often use the technique she suggests of a "DebugValueConverter", e.g.:
/// <summary>
/// Helps to debug bindings. Use like this: Content="{Binding PropertyName, Converter={StaticResource debugConverter}}"
/// </summary>
public class DebugConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
And then set a breakpoint in your converter, and watch what's happening. It's a hack and a kludge, but until we're all on SL5 (which has binding debugging built in), it's your best bet.
Ok, Sorted. I fixed the issue myself. Because I was updating the values in a loop, the ObservableCollection wasn't being updated properly. All I did in the beginning of the databinding method, was to Clear the collection : CallIndicators.Clear();

How can a ViewModel request data from a view when it needs it?

I have a calculated property on my View that I need to bind to my ViewModel. I'm using WPF and it seems that there is no way to make a bindable property (Dependency Property) that is self calculating. I don't want to perform the calculations whenever the View's state changes because they are time intensive. I want to do the calculations whenever the ViewModel needs the result, i.e. when it closes.
Based on your comment above, I'd use a Converter
Your ViewModel would contain the encrypted data, and the binding to the View uses a Converter which converts it into something readable. When it's time to save the data back to the ViewModel, use the ConvertBack method of the converter to encrypt the data again.
<TextBox Text="{Binding EncryptedAccountNumber,
Converter={StaticResource DecryptTextConverter}}" />
public class DecryptTextConverter: IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
// Implement decryption code here
return decryptedValue;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
// Implement encryption code here
return ecryptedValue;
}
}
If the Encryption code takes a while, set your UpdateSourceTrigger=Explicit and manually trigger the source update when the Save button is clicked.
This is my solution. It works the same way as ICommand but the view provides the delegate (CalculationDelegate) and the view model calls CanExecute and Execute. Its not pure MVVM but it works.
public interface ICalculationProvider<TResult>
{
event EventHandler CanExecuteChanged;
Func<TResult> CalculationDelegate { get; set; }
bool CanExecute();
TResult Execute();
bool TryExecute(out TResult a_result);
}
I have marked Rachel's answer as correct, simply because what I am doing here is not pure MVVM.

how and where to use DependencyProperty

I am new to WPF so after reading for a while I deduce that my problem needs to be handled with this pattern: DependencyProperty.
I want my ToggleButton to have another boolean property.
My problem is where should I assign this property, and how? Inside the object that is bound to the ToggleButton?
Let's say I have a class cell (which is bound to this Button) that when clicked I want that from this point on, it would hold new face with trigger on.
My new property will be:
bool wasClick
Can someone explain to me how I should write it and tell me more about this new concept?
EDIT:
The main topic is where should I define it so I want it asoocited to a Button but where should I write the code. Lets say I have a class that is bound to a Button. Should I write:
public static readonly DependencyProperty IsSpinningProperty =
DependencyProperty.Register(
... "IsSpinning", typeof(Boolean),
in this class or should I write it in my view model? If so, where and how?
As the name implies (kind of poorly), a dependency property is a property whose value can depend on something else. Generally, this means a property whose value gets determined automatically (and dynamically) by the WPF framework under certain conditions. The most common conditions are:
The property has a default value, or inherits its value from an ancestor in the visual tree. In this case, the property's value is determined without it ever being set.
The property is the target of data binding.
The property's value is set by an animation.
Not all properties whose value gets set by the WPF framework need to be dependency properties. Any CLR property with a public getter and setter can be the source of a two-way data binding.
In your case, it sounds like you don't really need a dependency property, not if you're using a view model. You could just do this (assuming that you've implemented property-change notification in your class):
private bool _IsChecked;
public bool IsChecked
{
get { return _IsChecked; } }
set
{
if (value == _IsChecked)
{
return;
}
_IsChecked = value;
WasChecked = WasChecked || value;
OnPropertyChanged("IsChecked");
}
}
private bool _WasChecked;
public bool WasChecked
{
get { return _WasChecked; }
private set
{
if (value == _WasChecked)
{
return;
}
_WasChecked = value;
OnPropertyChanged("WasChecked");
}
}

How can I make a WPF TreeView data binding lazy and asynchronous?

I am learning how to use data binding in WPF for a TreeView. I am procedurally creating the Binding object, setting Source, Path, and Converter properties to point to my own classes. I can even go as far as setting IsAsync and I can see the GUI update asynchronously when I explore the tree. So far so good!
My problem is that WPF eagerly evaluates parts of the tree prior to them being expanded in the GUI. If left long enough this would result in the entire tree being evaluated (well actually in this example my tree is infinite, but you get the idea). I would like the tree only be evaluated on demand as the user expands the nodes. Is this possible using the existing asynchronous data binding stuff in the WPF?
As an aside I have not figured out how ObjectDataProvider relates to this task.
My XAML code contains only a single TreeView object, and my C# code is:
public partial class Window1 : Window
{
public Window1() {
InitializeComponent();
treeView.Items.Add( CreateItem(2) );
}
static TreeViewItem CreateItem(int number)
{
TreeViewItem item = new TreeViewItem();
item.Header = number;
Binding b = new Binding();
b.Converter = new MyConverter();
b.Source = new MyDataProvider(number);
b.Path = new PropertyPath("Value");
b.IsAsync = true;
item.SetBinding(TreeView.ItemsSourceProperty, b);
return item;
}
class MyDataProvider
{
readonly int m_value;
public MyDataProvider(int value) {
m_value = value;
}
public int[] Value {
get {
// Sleep to mimick a costly operation that should not hang the UI
System.Threading.Thread.Sleep(2000);
System.Diagnostics.Debug.Write(string.Format("Evaluated for {0}\n", m_value));
return new int[] {
m_value * 2,
m_value + 1,
};
}
}
}
class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
// Convert the double to an int.
int[] values = (int[])value;
IList<TreeViewItem> result = new List<TreeViewItem>();
foreach (int i in values) {
result.Add(CreateItem(i));
}
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
throw new InvalidOperationException("Not implemented.");
}
}
}
Note: I have previously managed to do lazy evaluation of the tree nodes by adding WPF event handlers and directly adding items when the event handlers are triggered. I'm trying to move away from that and use data binding instead (which I understand is more in spirit with "the WPF way").
A generic solution (as I'm not sure if your code is not mock)
Create your model containing parent and children (In this case it is an int and a list of int)
Create a ViewModel having a property IsExpanded in addition to the Model's properties
Bind your IsExpanded property to the TreeViewItem's IsExpanded property in the view(xaml)
In the IsExpanded property's setter, fill in your Children list using the Dispatcher, the priority being Background. Each addition of item into your Children List should trigger the PropertyChanged event.
Check out the MVVM design pattern, if you're not familiar with. Here is a good video by Jason

WPF Property Data binding to negate the property

Is there any way to change the value of property at runtime in WPF data binding. Let's say my TextBox is bind to a IsAdmin property. Is there anyway I can change that property value in XAML to be !IsAdmin.
I just want to negate the property so Valueconverter might be an overkill!
NOTE: Without using ValueConverter
You can use an IValueConverter.
[ValueConversion(typeof(bool), typeof(bool))]
public class InvertBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool original = (bool)value;
return !original;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
bool original = (bool)value;
return !original;
}
}
Then you'd setup your binding like:
<TextBlock Text="{Binding Path=IsAdmin, Converter={StaticResource boolConvert}}" />
Add a resource (usually in your UserControl/Window) like so:
<local:InvertBooleanConverter x:Key="boolConvert"/>
Edit in response to comment:
If you want to avoid a value converter for some reason (although I feel that it's the most appropriate place), you can do the conversion directly in your ViewModel. Just add a property like:
public bool IsRegularUser
{
get { return !this.IsAdmin; }
}
If you do this, however, make sure your IsAdmin property setter also raises a PropertyChanged event for "IsRegularUser" as well as "IsAdmin", so the UI updates accordingly.
If you specifically want to do this at XAML end (I am not sure the reason for that, unless you have 100s of similar operation of negate) there are only two ways 1) Using IValueConverter 2)write a XAML Markup Extension (Way too much work for this small task :))
Then the other obvious way is to write another property in your ViewModel , which can return the Negative of the IsAdmin property.
You can't bind to !Property, but you could create a new Binding with an appropriate IValueConverter and change out the entire Binding at runtime. The key is the BindingOperations class, which allows you to change the binding on a particular DependencyProperty.
public static void InvertBinding(DependencyObject target, DependencyProperty dp)
{
//We'll invert the existing binding, so need to find it
var binding = BindingOperations.GetBinding(target, dp);
if (binding != null)
{
if (binding.Converter != null)
throw new InvalidOperationException("This binding already has a converter and cannot be inverted");
binding.Converter = new InvertingValueConverter(); //This would be your custom converter
//Not sure if you need this step, but it will cause the binding to refresh
BindingOperations.SetBinding(target, dp, binding);
}
}
This should give you a general idea; I wouldn't use this for production code, as you'd probably want to generalize it to toggle the converter or whatever else you need to change out at runtime. You could also avoid changing the binding entirely by creating a new property you bind to that encapsulates this 'switching' logic. The last option is probably the best.
You can write a ValueConverter that automatically negates the input before returning it. Have a look at BenCon's blog for a short reading on value converters.

Resources