【问题标题】:Binding all elements of ObservableCollection to TextBlock将 ObservableCollection 的所有元素绑定到 TextBlock
【发布时间】:2018-03-20 11:11:16
【问题描述】:

我正在尝试将 TextBlock 绑定到 ObservableCollection 中的项目。 TextBlock 值应由集合中的元素生成。集合中的元素数量在 0 到 7 之间(如果有帮助的话)。 MyClass 实现了 INotifyPropertyChanged。它应该直接是TextBlock,而不是ListBox。我该怎么做?谢谢!
更新:问题是我以前不知道集合中的元素数量。我知道在这种情况下使用 ListBox 或 ListView 会更好,但在 TextBlock 或 Label 中使用它很重要

例如:
1.ObservableCollection 包含元素 0、1、2。
TextBlock 应包含以下“值:0、1、2”
2. ObservableCollection 包含元素 0, 1.
TextBlock 应包含以下“值:0, 1”

   <TextBlock>
          <Run Text="Values: "/>
          <Run Text="{Binding Values}" />                 
   </TextBlock>


ObservableCollection<int> values = new ObservableCollection<int>();
        public ObservableCollection<int> Values
        {
            get => values;
            set
            {
                values = value;
                OnPropertyChanged();
            }
        }

【问题讨论】:

标签: c# wpf binding observablecollection textblock


【解决方案1】:

使用连接这些字符串的转换器:

public class StringsCollectionConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null) return null;
        return string.Join("\n", value as ObservableCollection<string>);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Xaml

<Window.Resources>
    <local:StringsCollectionConverter x:Key="StringsCollectionConverter"/>
</Window.Resources> 
<Grid>
    <TextBlock Text="{Binding TextBlockCollection, Converter={StaticResource StringsCollectionConverter}}"></TextBlock>
</Grid>

【讨论】:

  • 问题是,如果集合被改变,文本将不会被更新。
  • 是的,一定要使用双向绑定,实现INotifyPropertyChanged接口并调用OnPropertyChanged("TextBlockCollection");集合更改后
  • 我的意思是,如果同一个集合还有一个项目。
  • 我没有关注?
  • 你是对的!我的道歉!我撤消了我的反对票(但您可能需要更改答案才能正常工作)
【解决方案2】:

您必须使用转换器绑定到集合。
问题是在集合更改时更新值(这里我的意思不是将值设置为新集合,而是在集合中添加/删除项目)。
要在添加/删除时实现更新,您必须使用 MultiBinding 与 ObservableCollection.Count 的绑定之一,因此如果更改计数,则绑定属性将被更新。

<Window.Resources>
    <local:MultValConverter x:Key="multivalcnv"/>
</Window.Resources> 
<TextBlock>
    <TextBlock.Text>
        <MultiBinding Converter="{StaticResource multivalcnv}">
            <Binding Path="Values"/>
            <Binding Path="Values.Count"/>
        </MultiBinding>
    </TextBlock.Text>
</TextBlock>

public class MultValConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values.Length > 1 && values[0] is ICollection myCol)
        {
            var retVal = string.Empty;
            var firstelem = true;
            foreach (var item in myCol)
            {
                retVal += $"{(firstelem?string.Empty:", ")}{item}";
                firstelem = false;
            }

            return retVal;
        }
        else
            return Binding.DoNothing;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException("It's a one way converter.");
    }
}

【讨论】:

  • 这里绝对不需要MultiBinding!
  • 有了 MultiBinding,我不需要在每次修改集合时在 VewModel (No NotifyPropertyChanged) 中做任何事情。
  • 你没有使用 values[1],为什么还要传递它!
  • 对于MultiBinding 更新目标,如果Count 更改。
【解决方案3】:

创建一个额外的字符串属性,每次集合项更改时都会更改:

public class Vm
{
    public Vm()
    { 
       // new collection assigned via property because property setter adds event handler
       Values = new ObservableCollection<int>();
    }

    ObservableCollection<int> values;
    public ObservableCollection<int> Values
    {
        get => values;
        set
        {
            if (values != null) values.CollectionChanged -= CollectionChangedHandler;
            values = value;
            if (values != null) values.CollectionChanged += CollectionChangedHandler;
            OnPropertyChanged();
        }
    }

    private void CollectionChangedHandler(object sender, NotifyCollectionChangedEventArgs e)
    {
        OnPropertyChanged("ValuesText");
    }

    public string ValuesText
    {
        get { return "Values: " + String.Join(", ", values);}
    }
}

然后绑定到该属性:

<TextBlock Text="{Binding ValuesText}"/>

【讨论】:

  • 这绝对是在 ViewModel 中处理所有内容的选项。问题是它属于哪个更好(视图或视图模型)?
  • @Rekshino,VM。例如整个 ReactiveUI 框架,它使用左右这样的属性:见Output Propertiesanother example
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-05-23
  • 1970-01-01
  • 2018-05-14
  • 1970-01-01
  • 1970-01-01
  • 2011-04-10
  • 1970-01-01
相关资源
最近更新 更多