ブール値のプロパティを持つオブジェクトの ObservableCollection があります。
GUI には、IsChecked プロパティを各オブジェクトのブール プロパティにバインドする CheckBox があります。
XAML のみを使用することは可能ですか? どのように?
束縛だけでやりたい ループより入札の方が早い
これを試して:
<ListBox ItemsSource={Binding path}>
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding yourBoolPropertyName, Mode = TwoWay}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
これにより、コレクションにバインドするチェックボックスのリストが作成されます。もちろん、バインディング用のパスを適切に設定する必要があります。
ViewModelにboolプロパティを作成します。これは、すべてObjects
のObservableCollectionをループして、すべてのオブジェクトに対してプロパティがtrueであることを確認します。
public bool AllTrue
{
get
{
return Objects.All(o => o.Selected);
}
}
これがあなたObjects
のインスタンスでありObservableCollection
、Selected
オブジェクトのboolプロパティです。
XAML
<CheckBox IsChecked="{Binding AllTrue}"/>
次のような方法で、コントロール内のプロパティを項目のコレクションのプロパティにバインドできるようにする動作を作成しました。
アイテムのプロパティを変更すると、すべてのアイテムが同じプロパティを持つ場合、コントロールに反映されます。そうでない場合、コントロールのプロパティにはフォールバック値 (null など) が与えられます。
public class CollectionPropertyBehavior : Behavior<DependencyObject>
{
private IEnumerable<ValueProxy> proxies;
private bool syncking;
public string SourcePropertyPath
{
get { return (string)GetValue(SourcePropertyPathProperty); }
set { SetValue(SourcePropertyPathProperty, value); }
}
public static readonly DependencyProperty SourcePropertyPathProperty =
DependencyProperty.Register("SourcePropertyPath", typeof(string), typeof(CollectionPropertyBehavior), new PropertyMetadata(null));
public string CollectionPropertyPath
{
get { return (string)GetValue(CollectionPropertyPathProperty); }
set { SetValue(CollectionPropertyPathProperty, value); }
}
public static readonly DependencyProperty CollectionPropertyPathProperty =
DependencyProperty.Register("CollectionPropertyPath", typeof(string), typeof(CollectionPropertyBehavior), new PropertyMetadata(null));
private IEnumerable<object> Items { get { return this.ItemsSource == null ? null : this.ItemsSource.OfType<object>(); } }
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(CollectionPropertyBehavior), new PropertyMetadata(null, ItemsSourceChanged));
private object Value
{
get { return (object)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
private static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(object), typeof(CollectionPropertyBehavior), new PropertyMetadata(null, ValueChanged));
public object DefaultValue
{
get { return (object)GetValue(DefaultValueProperty); }
set { SetValue(DefaultValueProperty, value); }
}
public static readonly DependencyProperty DefaultValueProperty =
DependencyProperty.Register("DefaultValue", typeof(object), typeof(CollectionPropertyBehavior), new PropertyMetadata(null));
private static void ValueChanged(object sender, DependencyPropertyChangedEventArgs args)
{
var element = sender as CollectionPropertyBehavior;
if (element == null || element.ItemsSource == null) return;
element.UpdateCollection();
}
private static void ItemsSourceChanged(object sender, DependencyPropertyChangedEventArgs args)
{
var element = sender as CollectionPropertyBehavior;
if (element == null || element.ItemsSource == null) return;
element.ItemsSourceChanged();
}
private void ItemsSourceChanged()
{
this.proxies = null;
if (this.Items == null || !this.Items.Any() || this.CollectionPropertyPath == null) return;
// Cria os proxies
this.proxies = this.Items.Select(o =>
{
var proxy = new ValueProxy();
proxy.Bind(o, this.CollectionPropertyPath);
proxy.ValueChanged += (s, e) => this.UpdateSource();
return proxy;
}).ToArray();
this.UpdateSource();
}
private void UpdateSource()
{
if (this.syncking) return;
// Atualiza o valor
using (new SynckingScope(this))
{
object value = this.proxies.First().Value;
foreach (var proxy in this.proxies.Skip(1))
{
value = object.Equals(proxy.Value, value) ? value : this.DefaultValue;
}
this.Value = value;
}
}
private void UpdateCollection()
{
// Se o valor estiver mudando em função da atualização de algum
// elemento da coleção, não faz nada
if (this.syncking) return;
using (new SynckingScope(this))
{
// Atualiza todos os elementos da coleção,
// atrávés dos proxies
if (this.proxies != null)
foreach (var proxy in this.proxies)
proxy.Value = this.Value;
}
}
protected override void OnAttached()
{
base.OnAttached();
// Bind da propriedade do objeto fonte para o behavior
var binding = new Binding(this.SourcePropertyPath);
binding.Source = this.AssociatedObject;
binding.Mode = BindingMode.TwoWay;
BindingOperations.SetBinding(this, ValueProperty, binding);
}
protected override void OnDetaching()
{
base.OnDetaching();
// Limpa o binding de value para a propriedade do objeto associado
this.ClearValue(ValueProperty);
}
internal class SynckingScope : IDisposable
{
private readonly CollectionPropertyBehavior parent;
public SynckingScope(CollectionPropertyBehavior parent)
{
this.parent = parent;
this.parent.syncking = true;
}
public void Dispose()
{
this.parent.syncking = false;
}
}
internal class ValueProxy : DependencyObject
{
public event EventHandler ValueChanged;
public object Value
{
get { return (object)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(object), typeof(ValueProxy), new PropertyMetadata(null, OnValueChanged));
private static void OnValueChanged(object sender, DependencyPropertyChangedEventArgs args)
{
var element = sender as ValueProxy;
if (element == null || element.ValueChanged == null) return;
element.ValueChanged(element, EventArgs.Empty);
}
public void Bind(object source, string path)
{
// Realiza o binding de value com o objeto desejado
var binding = new Binding(path);
binding.Source = source;
binding.Mode = BindingMode.TwoWay;
BindingOperations.SetBinding(this, ValueProperty, binding);
}
}
}
次のように使用できます。
<CheckBox>
<i:Interaction.Behaviors>
<local:CollectionPropertyBehavior CollectionPropertyPath="MyBooleanProperty" SourcePropertyPath="IsChecked" ItemsSource="{Binding CollectionInViewModel}"/>
</i:Interaction.Behaviors>
</CheckBox>
コレクションの変更はまだサポートされていません (コレクションのスワップのみ) が、簡単に変更できると思います。そのまま使用する場合は、ObservableCollection の CollectionChanged イベントにハンドラーを追加するだけで、ItemsSource の更新がトリガーされます。
observableCollection.CollectionChanged += (s,e) => this.OnPropertyChanged("ItemsSource);
ここに別の例を投稿しました。