【WPF】ListView绑定自定义的ObservableDictionary,绑定 DataTemplate内的控件时候,命令会失效
自定义类ObservableDictionary
注意:
(1)绑定字典时候要用Value.字段例如: Text="{Binding Value.Close, StringFormat={}{0:F2}}">, StringFormat={}{0:F2}是格式化字段,格式化字段请看WPF编程宝典20章
StringFormat, 当格式化字符位于开始时, 比如StringFormat={0:C}这样子的话, 必须前缀加上{}, 不然显示失败。原因是不让系统误认为是标记扩展。 正确的结果就是StringFormat={}{0:C}
using System; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Spider.Comment { using System; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Runtime.CompilerServices; namespace ObservableDictionary { public delegate void NotifyDictionaryChangedEventHandler(object sender, NotifyDictionaryChangedEventArgs e); public class NotifyDictionaryChangedEventArgs { public NotifyCollectionChangedAction Action { get; } public object AddedKey { get; } public object AddedItem { get; } public object RemovedKey { get; } public object RemovedItem { get; } public NotifyDictionaryChangedEventArgs(NotifyCollectionChangedAction action) { this.Action = action; } public NotifyDictionaryChangedEventArgs(NotifyCollectionChangedAction action, object changedItem, object key) { this.Action = action; this.AddedItem = changedItem; this.AddedKey = key; } public NotifyDictionaryChangedEventArgs(NotifyCollectionChangedAction action, object addedItem, object addedKey, object removedItem, object removedKey) { this.Action = action; this.AddedItem = addedItem; this.AddedKey = addedKey; this.RemovedItem = removedItem; this.RemovedKey = removedKey; } } public interface INotifyDictionaryChanged { event NotifyDictionaryChangedEventHandler DictionaryChanged; } public interface IObservableMap<TValue> : IEnumerable<KeyValuePair<string, TValue>>, INotifyDictionaryChanged { TValue this[string key] { get; set; } bool ContainsKey(string key); void Remove(string key); void Add(string key, TValue value); } public class ObservableStringDictionary<TValue> : Dictionary<string, TValue>, IObservableMap<TValue>, INotifyDictionaryChanged, INotifyPropertyChanged { #region public public new TValue this[string key] { get => base[key]; set { base[key] = value; this.OnPropertyChanged(CountString); this.OnPropertyChanged(IndexerNameBeg + IndexerNameEnd); this.OnPropertyChanged(IndexerNameBeg + key + IndexerNameEnd); this.OnCollectionChanged(NotifyCollectionChangedAction.Reset, key, value); } } public new void Add(string key, TValue value) { base.Add(key, value); this.OnPropertyChanged(CountString); this.OnPropertyChanged(IndexerNameBeg + IndexerNameEnd); this.OnPropertyChanged(IndexerNameBeg + key + IndexerNameEnd); this.OnCollectionChanged(NotifyCollectionChangedAction.Add, key, value); } public new void Clear() { base.Clear(); this.OnPropertyChanged(CountString); this.OnPropertyChanged(IndexerNameBeg + IndexerNameEnd); this.OnCollectionReset(); } public new void Remove(string key) { TValue removedItem = this[key]; base.Remove(key); this.OnPropertyChanged(CountString); this.OnPropertyChanged(IndexerNameBeg + IndexerNameEnd); this.OnPropertyChanged(IndexerNameBeg + key + IndexerNameEnd); this.OnCollectionChanged(NotifyCollectionChangedAction.Remove, key, removedItem); } event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged { add => this.PropertyChanged += value; remove => this.PropertyChanged -= value; } #if !FEATURE_NETCORE [field: NonSerializedAttribute()] #endif public virtual event NotifyDictionaryChangedEventHandler DictionaryChanged; #if !FEATURE_NETCORE [field: NonSerializedAttribute()] #endif protected virtual event PropertyChangedEventHandler PropertyChanged; private void OnCollectionChanged(NotifyCollectionChangedAction action, string key, TValue value) { this.OnCollectionChanged(new NotifyDictionaryChangedEventArgs(action, value, key)); } protected virtual void OnCollectionChanged(NotifyDictionaryChangedEventArgs e) { if (this.DictionaryChanged is null) return; using (this.BlockReentrancy()) { this.DictionaryChanged(this, e); } } protected IDisposable BlockReentrancy() { this._monitor.Enter(); return this._monitor; } protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) { this.PropertyChanged?.Invoke(this, e); } #endregion #region private private void OnPropertyChanged(string propertyName) { this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); } private void OnCollectionReset() { this.OnCollectionChanged(new NotifyDictionaryChangedEventArgs(NotifyCollectionChangedAction.Reset)); } #endregion Private Methods #region Private Types // this class helps prevent reentrant calls #if !FEATURE_NETCORE [Serializable()] [TypeForwardedFrom("WindowsBase, Version=3.0.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")] #endif private class SimpleMonitor : IDisposable { public void Enter() { ++this._busyCount; } public void Dispose() { --this._busyCount; } public bool Busy { get => this._busyCount > 0; } int _busyCount; } #endregion Private Types #region Private Fields private const string CountString = "Count"; // This must agree with Binding.IndexerName. It is declared separately // here so as to avoid a dependency on PresentationFramework.dll. private const string IndexerNameBeg = "Item["; private const string IndexerNameEnd = "]"; private SimpleMonitor _monitor = new SimpleMonitor(); #endregion Private Fields } } }
绑定ListView,
注意 GridViewColumn 用了 DisplayMemberBinding ,使得CellTemplate失效。 两者的这能选一个,DisplayMemberBinding 权重大于CellTemplate
(1)绑定<DataTemplate>内的控件时候,命令会失效,要把命令直接绑定到 元数据所绑定的控件的DataContext上
一定要这样写Command="{Binding DataContext.AddSelfselectionStockCommand, ElementName=dashboard ,Mode=TwoWay}"
(2)、<DataTemplate>标签内的命令 ,要使用路由命令或者自定义命令连接到命令管理器如下:
public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } }
<ListView ItemContainerStyle="{DynamicResource ListViewItemContainerStyle}" ItemsSource="{Binding AllBoardStocksData}" VirtualizingPanel.VirtualizationMode="Recycling" VirtualizingPanel.IsVirtualizing="True" Background="Transparent" BorderThickness="0" > <ListView.View > <GridView ColumnHeaderContainerStyle="{DynamicResource ColumnHeaderContainerStyle}" > <GridViewColumn Width="100"> <GridViewColumnHeader Style="{DynamicResource HeaderGridViewColumnHeaderStyle}" Cursor="Hand" Content="代码" /> <GridViewColumn.CellTemplate > <DataTemplate > <TextBlock Margin="10" Background="Gray" Text="{Binding Value.Code}"></TextBlock> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> <GridViewColumn Header="名称" DisplayMemberBinding="{Binding Value.Name }" /> <GridViewColumn Header="现价"> <GridViewColumn.CellTemplate> <DataTemplate> <TextBlock x:Name="CloseTextBoxStyle" Text="{Binding Value.Close, StringFormat={}{0:F2}}"></TextBlock> <DataTemplate.Triggers> <DataTrigger Binding="{Binding Value.Chg_rate, Converter={StaticResource IntToboolConverter}}" Value="true"> <Setter TargetName="CloseTextBoxStyle" Property="Foreground" Value="red"/> </DataTrigger> <DataTrigger Binding="{Binding Value.Chg_rate, Converter={StaticResource IntToboolConverter}}" Value="false"> <Setter TargetName="CloseTextBoxStyle" Property="Foreground" Value=" green"/> </DataTrigger> </DataTemplate.Triggers> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> <GridViewColumn Header="涨跌幅"> <GridViewColumn.CellTemplate> <DataTemplate> <TextBlock x:Name="UpDowpTextBoxStyle" Text="{Binding StringFormat={}{0}%,Path= Value.Chg_rate}"></TextBlock> <DataTemplate.Triggers> <DataTrigger Binding="{Binding Value.Chg_rate, Converter={StaticResource IntToboolConverter}}" Value="true"> <Setter TargetName="UpDowpTextBoxStyle" Property="Foreground" Value="red"/> </DataTrigger> <DataTrigger Binding="{Binding Value.Chg_rate, Converter={StaticResource IntToboolConverter}}" Value="false"> <Setter TargetName="UpDowpTextBoxStyle" Property="Foreground" Value=" green"/> </DataTrigger> </DataTemplate.Triggers> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> <GridViewColumn Header="总市值(亿)" DisplayMemberBinding="{Binding Value.TotalValue,StringFormat=000.000E+0010}" /> <GridViewColumn Header="加入自选股"> <GridViewColumn.CellTemplate> <DataTemplate> <CheckBox Name="isselectstock" IsThreeState="False" IsChecked="{Binding Value.IsSelfselectionStock }" Command="{Binding DataContext.AddSelfselectionStockCommand, ElementName=dashboard ,Mode=TwoWay}" > <CheckBox.CommandParameter> <MultiBinding Converter="{StaticResource CodeAndCheckboxStateMultiConverter}"> <Binding Path="IsChecked" ElementName="isselectstock"/> <Binding Path="Value.Code" /> </MultiBinding> </CheckBox.CommandParameter> </CheckBox> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> <GridViewColumn Header="详情"> <GridViewColumn.CellTemplate> <DataTemplate> <Button >详情</Button> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> </GridView> </ListView.View> </ListView>
3、Listview 绑定复杂的对象,并且显示复杂的对象。
此时代码中不能用DisplayMemberBinding ,而要用CellTemplate 。在该案例CellTemplate的数据上下文是LinePoco,所以模板中要具体到列(属性)中就是ALine等,该属性对应是类型是PhonogramPoco
public class PhonogramPoco { /// <summary> /// 平假名 /// </summary> public string? Hiragana { get; set; } /// <summary> /// 片假名 /// </summary> public string? Katakana { get; set; } /// <summary> /// 音标 /// </summary> public string? Soundmark { get; set; } /// <summary> /// 读音 /// </summary> public string? SoundUri { get; set; } } public class linePoco { public PhonogramPoco? ALine { set; get; }=null; public PhonogramPoco? ILine { set; get; }=null; public PhonogramPoco? ULine { set; get; }=null; public PhonogramPoco? ELine { set; get; }=null; public PhonogramPoco? OLine { set; get; }=null; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗
2021-09-24 自定义表链 SnakList