16

I'm trying to bind a TextBlock to a specific element in an ObservableCollection. This is what I do right now:

private ObservableCollection<double> arr = new ObservableCollection<double>();
public ObservableCollection<double> Arr { get { return arr; } set { arr = value; }  }

testBox.DataContext = this;

private void Button_Click(object sender, RoutedEventArgs e)
{
   Arr[0] += 1.0;
}

    [ValueConversion(typeof(ObservableCollection<double>), typeof(String))]
    public class myObsCollConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            ObservableCollection<double> l = value as ObservableCollection<double>;
            if( l == null )
                return DependencyProperty.UnsetValue;
            int i = int.Parse(parameter.ToString());

            return l[i].ToString();
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return DependencyProperty.UnsetValue;
        }
    }


    <Window.Resources>
        <local:myObsCollConverter x:Key="myConverter"/>
    </Window.Resources>

        <TextBlock Name="testBox" Text="{Binding Path=Arr,Converter={StaticResource myConverter}, ConverterParameter=0}" />

What I see is that testBox shows the first value of Arr when it is created. But it doesn't reflect any changes to this element. What do I have to do in order to see changes to Arr[0] in my textBox?

4 Answers 4

29

No need for the converter. You can bind directly to Arr[0] like this

 <TextBlock Name="testBox" Text="{Binding Path=Arr[0]}"/>

The elements in Arr would need to implement INotifyPropertyChanged though in order to dynamically update.

Update: To elaborate a bit more:

public class MyDouble : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private double _Value;
    public double Value 
    { 
        get { return _Value; } 
        set { _Value = value; OnPropertyChanged("Value"); }
    }

    void OnPropertyChanged(string propertyName)
    {
       var handler = PropertyChanged;
       if (handler != null)
       {
          handler(this, new PropertyChangedEventArgs(propertyName));
       }
    }
}

and then

 ObservableCollection<MyDouble> Arr { get; set; }

and bind to

 <TextBlock Name="testBox" Text="{Binding Path=Arr[0].Value}"/>
Sign up to request clarification or add additional context in comments.

4 Comments

Shouldn't it work anyway though? The ObservableCollection will raise a CollectionChanged event when the value is replaced... I'm surprised the binding doesn't notice that.
Well, you bind to the element and not the collection so I guess the UI will only listen to the PropertyChanged event
What if I wan't to bind the element index (0) to another control? e.g. I have a ComboBox to select the index of the element, which my testBox should display.
Based on that solution (ie Use Observable collection and bind directly to Arr[0]" I had to define a event callback on Arr.CollectionChanged, in which I called OnPropertyChanged("Arr"). That way the binding can work when a element changes.
7

ObservableCollections do not propagate changes to values stored within objects that belong to the collection. It only fires off the notifications when the content of the collection itself changes (ie. item added, removed, reordered). If you want to make it so that your UI updates when values within the collection changes then you'll have to wire that up yourself separately.

1 Comment

This is replacing the item though, not modifying the internal data in an object. I've just checked, and it will fire the CollectionChanged event in this case (with an action of Replace).
0

you can use this way in my case I want to binding the visibility from an boolean array: code behind:

using System.Windows;
public static readonly DependencyProperty ButtonVisibleProperty =
        DependencyProperty.Register("ButtonVisible", typeof(BindingList<Boolean>), typeof(BaseWindow), new PropertyMetadata(new BindingList<Boolean>()));
 public BindingList<Boolean> ButtonVisible 
    { 
        get { return (BindingList<Boolean>)GetValue(BaseWindow.ButtonVisibleProperty); }
        set 
        {
            SetValue(BaseWindow.ButtonVisibleProperty, value);
            OnPropertyChanged("ButtonVisible");  
        } 
    }

and in Xaml:

Visibility=""{Binding Path=ButtonVisible[0], RelativeSource={RelativeSource AncestorType={x:Type Window}}}"

beware! the ancestorType depend of your Ancestor in my case is a window

Comments

0

To throw in my tu'pence, as Avalonia beginner, using ReactiveObject.

I've found it will render bindings with an array if it is recreated.

I've just implemented something like volume control bars (it's actually the loading on a crane).

In the C#:

private const int NumberOfBars = 9;

// has to recreate the array so Avalonia will render
LoadBarsLevel = Enumerable.Repeat(false, NumberOfBars).ToArray(); 
var level = (int)(args.LoadingPercentage / (100.0 / NumberOfBars));
Enumerable.Repeat(true, Math.Min(level, 
NumberOfBars)).ToArray().CopyTo(LoadBarsLevel, 0);

Property declaration:

public bool[] LoadBarsLevel { get; private set; } = Enumerable.Repeat(false, NumberOfBars).ToArray();

And in a base class:

    foreach (var prop in 
       GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
        this.RaisePropertyChanged(prop.Name);

And in the XAML (imagine 9 like this with different positions from Top):

  <Panel Canvas.Left="23" Canvas.Top="455" Width="50" Height="50">
      <Image Source="/Assets/LevelGrey.png" />
  </Panel>
  <Panel Canvas.Left="23" Canvas.Top="455" Width="50" Height="50" IsVisible="{Binding LoadBarsLevel[1]}">
      <Image Source="/Assets/LevelGreen.png" />
  </Panel>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.