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();
Related
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.
I create a VM based on MVVM light toolkit.
In VM, there is a simple ICommand(RelayCommand)
private RelayCommand _myCommand = null;
public RelayCommand MyCommand
{
get
{
if (_myCommand == null) //set break point here for debug
{
_myCommand = new RelayCommand(() =>
{
try
{
//....
}
catch (Exception ex)
{
// notify user if there is any error
//....
}
}
, () => true);
}
return _myCommand;
}
}
then in xaml, just bind this Command property to a button like:
<Button Grid.Column="1" x:Name="Test" Content="Test" Margin="2,0,2,0" Command="{Binding Path=MyCommand}" />
Then run the app, and click on the button, there is no response at all. No error.
VM is working fine. The data has been loaded to a datagrid before I click on the Test button.
If debug the app and put break point, the point is never reached.
How to resolve this problem?
Add a setter to your MyCommand property.
As always, check the Output window for any data binding errors when the XAML is rendered.
Also, try adding a test value converter and putting a breakpoint in the convert method to see if data binding is even being executed on that command. If the breakpoint isn't hit, you know you have a problem in your XAML. If the breakpoint is hit, take a look at the value to see if the data context is correct.
<UserControl.Resources>
<ResourceDictionary>
<TestConverter x:Key="TestConverter" />
</ResourceDictionary>
<Button Grid.Column="1" x:Name="Test" Content="Test" Margin="2,0,2,0" Command="{Binding Path=MyCommand, Converter={StaticResource TestConverter}}" />
</UserControl>
Test value converter - very useful for debugging data binding issues.
public class TestConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Debug.WriteLine("TestConverter.Convert(value := {0}, targetType := {1}, parameter := {2}, culture := {3})",
value, targetType, parameter, culture);
return value; // put break point here to test data binding
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
Debug.WriteLine("TestConverter.ConvertBack(value := {0}, targetType := {1}, parameter := {2}, culture := {3})",
value, targetType, parameter, culture);
return value;
}
}
Works on my machine :)
Seriously, I made a simple project, created a ViewModel, pasted in your code, and it worked. I am guessing you are dealing with some other issue.
Here is my C# code.
Here is my XAML code.
Time to evangelize a bit
This ViewModel code reeks. You might consider using some sort MVVM framework or helpers. If you look at ViewModelSupport, for instance, you can write your ViewModel like this:
public class MyViewModel : ViewModelBase
{
public void Execute_MyCommand()
{
// Your execution code here
}
}
Then, you avoid all that messy plumbing. Just think about it :)
the code looks fine. so you just have to check the output window for databinding errors. maybe you did not set the datacontext of the view correct. btw you should add your breakpoint in the try-catch of the command.
1) Make sure you're returning true from the relay command's CanExecute delegate. (I see you are doing this but good to double check).
2) Is the button inside a ListBox, DataGrid or DataForm?
For a ListBox or DataGrid:
If so you need to modify your binding expression to refer to the VM DataContext as opposed to the databound item. See this answer.
For a DataForm :
More tricky, but look at this question.
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
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.
Here's a basic example to explain my problem. Let's say I have
ObservableCollection<int> Numbers {get; set;}
and an IValueConverter that returns the sum of Numbers.
Normally what I'd do is changed the IValueConverter into an IMultiValueConverter and bind a second value to Numbers.Count like this
<MultiBinding Converter="{StaticResource SumTheIntegersConverter}">
<Binding Path="Numbers" />
<Binding Path="Numbers.Count" />
</MultiBinding>
However I'm unable to use this method to solve my actual problem. It seems like there should be a better way to update the binding when the collection changes that I'm just not thinking of. What's the best way to get the value converter to run when items are added and removed to Numbers?
This is actually surprisingly very difficult. An IValueConverter doesn't update, so this does not work as you'd hope.
I wrote a sample on the Microsoft Expression Gallery called Collection Aggregator that shows a working, if convoluted, approach to making this work via a Behavior that does the aggregation (Count, in your case, although I also support Sum, Average, etc) for you, instead of a converter.
In your model, subscribe to CollectionChanged and raise PropertyChanged:
Numbers.CollectionChanged += (o,e) =>
OnPropertyChanged(new PropertyChangedEventArgs(nameof(Numbers)));
And, as thomasgalliker mentioned, you should unsubscribe from the event when the model containing the connection is no longer used.
I ended up doing something like this which seems to work. It's far from an optimal solution and I'd still be interested in something better but it seems to work for my purposes.
class CollectionChangedHandlingValueConverter : IValueConverter
{
DependencyObject myTarget;
DependencyProperty myTargetProperty;
//If this ever needs to be called from XAML you can make it a MarkupExtension and use ProvideValue to set up the Target and TargetProperty
public CollectionChangedHandlingValueConverter(DependencyObject target, DependencyProperty dp)
{
myTarget = target;
myTargetProperty = dp;
}
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
INotifyCollectionChanged collection = value as INotifyCollectionChanged;
if (collection != null)
{
//It notifies of collection changed, try again when it changes
collection.CollectionChanged += DataCollectionChanged;
}
//Do whatever conversions here
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
void DataCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if ((myTarget != null) && (myTargetProperty != null))
{
BindingOperations.GetBindingExpressionBase(myTarget, myTargetProperty).UpdateTarget();
}
}
}
And I ended up synchronizing collection (original with converter), take a look at the buttom of my post for example:
http://alexburtsev.wordpress.com/2011/03/05/mvvm-pattern-in-silverlight-and-wpf/