需要创建一个自定义视图并将其附加到一个视图模型,该模型提供两个可观察的集合以及用于构建集合的函数。 集合内容显示在spinbox和colorpick控件中。 我的代码现在可以正常工作,但是我不确定这是实现绑定的正确方法。
observablecollection通过DependencyProperty绑定,但是我看到有人提到使用INotifyCollectionChanged和ICollectionView,但无法理解它们如何获取集合的单个项目。
public partial class BinsColorControl : UserControl, INotifyPropertyChanged
{
public ObservableCollection<Color> BinsColors
{
get { return (ObservableCollection<Color>)GetValue(BinsColorProperty); }
set { SetValue(BinsColorProperty, value); }
}
public ObservableCollection<double> BinsValues
{
get { return (ObservableCollection<double>)GetValue(BinsValueProperty); }
set { SetValue(BinsValueProperty, value); }
}
public static readonly DependencyProperty BinsColorProperty =
DependencyProperty.RegisterAttached("BinsColors", typeof(ObservableCollection<Color>),
typeof(BinsColorControl),
new UIPropertyMetadata(null, OnBinsColorCollectionChanged));
public static readonly DependencyProperty BinsValueProperty =
DependencyProperty.Register("BinsValues", typeof(ObservableCollection<double>),
typeof(BinsColorControl),
new UIPropertyMetadata(null, OnBinsValueCollectionChanged));
private static void OnBinsColorCollectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
BinsColorControl binsColorControl = d as BinsColorControl;
var action = new NotifyCollectionChangedEventHandler(
(o, args) =>
{
if (binsColorControl != null)
{
binsColorControl._reloadUI();
}
}
);
if (binsColorControl != null)
{
if (e.OldValue != null)
{
var coll = (INotifyCollectionChanged)e.OldValue;
// Unsubscribe from CollectionChanged on the old collection
coll.CollectionChanged -= action;
}
if (e.NewValue != null)
{
var coll = (ObservableCollection<Color>) e.NewValue;
// Subscribe to CollectionChanged on the new collection
coll.CollectionChanged += action;
binsColorControl._reloadUI();
}
binsColorControl.OnPropertyChanged("NOBins");
}
}
private static void OnBinsValueCollectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
BinsColorControl binsColorControl = d as BinsColorControl;
var action = new NotifyCollectionChangedEventHandler(
(o, args) =>
{
if (binsColorControl != null)
{
binsColorControl._reloadUI();
}
}
);
if (binsColorControl != null)
{
if (e.OldValue != null)
{
var coll = (INotifyCollectionChanged)e.OldValue;
coll.CollectionChanged -= action;
}
if (e.NewValue != null)
{
var coll = (ObservableCollection<double>)e.NewValue;
// Subscribe to CollectionChanged on the new collection
coll.CollectionChanged += action;
binsColorControl._reloadUI();
}
}
}
private void _reloadUI()
{
....
}
}
realoadUI方法将刷新或添加控件,为了更加清晰起见,我可能会对其进行更改:
private void _reloadUI()
{
if (BinsColors == null || BinsValues == null)
return;
#region colorPickers
while (_colorPickers.Count > NOBins)
{
binStack.Children.Remove(_colorPickers.Last());
_colorPickers.Remove(_colorPickers.Last());
}
for (int i = 0; i < NOBins; i++)
{
ColorPicker colPick;
if (i < _colorPickers.Count)
{
colPick = _colorPickers[i];
}
else
{
colPick = new ColorPicker();
colPick.Margin = new Thickness(5);
_colorPickers.Add(colPick);
}
colPick.SelectedColorChanged -= _onBinsColorChanged;
colPick.SelectedColor = BinsColors.ElementAt(i);
colPick.SelectedColorChanged += _onBinsColorChanged;
}
#endregion colorPickers
#region valuePickers
while (_valuePickers.Count > 0 && _valuePickers.Count > NOBinsValues)
{
binStack.Children.Remove(_valuePickers.Last());
_valuePickers.Remove(_valuePickers.Last());
}
for (int i = 0; i < NOBinsValues; i++)
{
DoubleUpDown valPick;
if (i < _valuePickers.Count)
{
valPick = _valuePickers[i];
}
else
{
valPick = new DoubleUpDown();
valPick.Margin = new Thickness(5);
_valuePickers.Add(valPick);
}
valPick.ValueChanged -= _onBinsValueChanged;
valPick.Value = BinsValues.ElementAt(i);
valPick.ValueChanged += _onBinsValueChanged;
}
#endregion valuePickers
if (NOBins > 0)
{
binStack.Children.Clear();
for (int i = 0; i < NOBins - 1; i++)
{
binStack.Children.Add(_colorPickers[i]);
if(NOBinsValues > i)
binStack.Children.Add(_valuePickers[i]);
}
if (_colorPickers.Count == NOBins)
{
binStack.Children.Add(_colorPickers[NOBins - 1]);
}
}
}
与此结合使用的视图模型称为analysisViewModel:
public class analysisViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler CollectionsChanged;
public ObservableCollection<double> BinsValues { get; private set; }
private void BinsValues_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
CollectionsChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(BinsValues)));
}
public ObservableCollection<Color> BinsColors { get; private set; }
private void BinsColors_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
CollectionsChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(BinsColors)));
}
....
public ColorBinsViewModel()
{
BinsValues = new ObservableCollection<double>();
BinsValues.CollectionChanged += BinsValues_CollectionChanged;
BinsColors = new ObservableCollection<Color>();
BinsColors.CollectionChanged += BinsColors_CollectionChanged;
NOBins = 2;
...
}
public int NOBins
{
get
{
return BinsColors.Count;
}
set
{
if (value >= 0 && value < 25)
{
if (BinsColors.Count > value)
{
while (value < BinsColors.Count)
{
BinsColors.RemoveAt(BinsColors.Count - 1);
BinsValues.RemoveAt(BinsValues.Count - 1);
}
if (BinsColors.Count < value)
{
while (BinsColors.Count < value - 1)
{
BinsColors.Add(new Color());
BinsValues.Add(new double());
BinsValues[BinsValues.Count - 1] = 0.0;
}
BinsColors.Add(new Color());
}
}
}
}
}
}
自定义视图当前在我用来创建标签的网格内使用。 该内容视图使用viewmodel和自定义视图,它们通过后面的代码连接在一起,我将viewmodel用于ui的可视化部分,因此无法快速分解。 customView之间的绑定是通过在网格构造函数中设置datacontext来执行的,如下所示:
public partial class MapAnalyseTabContent : Grid, INotifyPropertyChanged
{
...
public MapAnalyseTabContent()
{
InitializeComponent();
AnalysisViewModel = new analysisViewModel ();
binsColorsCtrl.DataContext = AnalysisViewModel;
...
}
... }
带有xaml一侧:
<Grid
xmlns:CustomControl="clr-namespace:MyApp.CustomControl"
...>
...
<CustomControl:BinsColorControl x:Name="binsColorsCtrl" BinsColors="{Binding BinsColors}" BinsValues="{Binding BinsValues}"/>
</Grid>
这是自定义视图的xaml代码
<UserControl x:Class="MyApp.CustomControl.BinsColorControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MyApp.CustomControl"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d"
d:DesignHeight="450" Width="139.423">
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Top">
<xctk:IntegerUpDown x:Name="NOBinsControl" Value="{Binding NOBins}" Margin="4,10,4,10" ></xctk:IntegerUpDown>
<StackPanel x:Name="binStack" HorizontalAlignment="Stretch" Margin="0" VerticalAlignment="Top">
<xctk:ColorPicker x:Name="colorPick1" VerticalAlignment="Top" Margin="4,2,4,2"/>
<xctk:DoubleUpDown x:Name="doubleSpin_bin1" VerticalAlignment="Top" Margin="4,2,4,2"/>
<xctk:ColorPicker x:Name="colorPick2" VerticalAlignment="Top" Margin="4,2,4,2"/>
<xctk:DoubleUpDown x:Name="doubleSpin_bin2" VerticalAlignment="Top" Margin="4,2,4,2"/>
<xctk:ColorPicker x:Name="colorPick3" VerticalAlignment="Top" Margin="4,2,4,2"/>
</StackPanel>
</StackPanel>
</UserControl>