C# 如果字典填写过迟,则绑定不能与字典一起使用
我有一个WPF应用程序。我使用的是MVVM模式。我有一本字典:C# 如果字典填写过迟,则绑定不能与字典一起使用,c#,wpf,mvvm,binding,C#,Wpf,Mvvm,Binding,我有一个WPF应用程序。我使用的是MVVM模式。我有一本字典: public abstract class ViewModelBase { public static Dictionary<string, Action> Permissions { get; set; } ... } public abstract class ViewModelBase { protected static SilentDictionary<s
public abstract class ViewModelBase
{
public static Dictionary<string, Action> Permissions { get; set; }
...
}
public abstract class ViewModelBase
{
protected static SilentDictionary<string, Action> _permissions;
public static SilentDictionary<string, Action> Permissions
{
get { return _permissions; }
set
{
_permissions = value;
NotifyPropertyChanged();
}
}
public static event EventHandler PermissionsChanged;
protected static void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
EventHandler temp = Volatile.Read(ref PermissionsChanged);
PermissionsChanged?.Invoke(null, new EventArgs());
}
// other code
}
公共抽象类ViewModelBase
{
公共静态字典权限{get;set;}
...
}
我想将它的值绑定到菜单项的可见性,如下所示:
<MenuItem x:Name="systemMenuItem" Header="System" Visibility="{Binding Permissions[systemMenuItem].CanBeShow, Converter={StaticResource BoolToVis}}">...</MenuItem>
。。。
要填充此字典,我需要窗口构建可视化树,因为字典的元素包含来自窗口菜单项的信息。如果我在初始化Component之前创建dictionary,我将得到一个异常,即没有值为
systemMenuItem
的键,因为VisualTreeHelper.GetChildrenCount
返回零元素。如果我在Loaded
事件中这样做,我将得到正常的填充字典,但在这种情况下绑定不起作用。在向用户显示窗口并从菜单项获取信息之前,我如何填写词典?在这种情况下,我怎样才能使我的装订工作正常呢?该窗口是主窗口和启动窗口。您可以定义如下的多值转换器:
public class ElementPermissionToVisibilityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
Dictionary<string, Action> permissions = values.OfType<Dictionary<string, Action>>().FirstOrDefault();
FrameworkElement element = values.OfType<FrameworkElement>().FirstOrDefault();
if (permissions != null && element != null && !string.IsNullOrWhiteSpace(element.Name))
{
Action action;
if (permissions.TryGetValue(element.Name, out action))
{
return action.CanBeShown ? Visibility.Visible : Visibility.Collapsed;
}
else
{
return Visibility.Collapsed;
}
}
else
{
return DependencyProperty.UnsetValue;
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
<MenuItem x:Name="systemMenuItem" Header="System">
<MenuItem.Visibility>
<MultiBinding Converter="{StaticResource ElementPermissionToVisibilityConverter}">
<Binding Path="Permissions"/>
<Binding RelativeSource="{RelativeSource Self}"/>
</MultiBinding>
</MenuItem.Visibility>
</MenuItem>
公共类元素PermissionToVisibilityConverter:IMultiValueConverter
{
公共对象转换(对象[]值,类型targetType,对象参数,CultureInfo区域性)
{
字典权限=value.OfType().FirstOrDefault();
FrameworkElement元素=value.OfType().FirstOrDefault();
if(权限!=null&&element!=null&&string.IsNullOrWhiteSpace(element.Name))
{
行动;
if(permissions.TryGetValue(element.Name,out操作))
{
return action.CanBeShown?可见性。可见:可见性。折叠;
}
其他的
{
返回可见性。折叠;
}
}
其他的
{
返回dependencProperty.unset值;
}
}
公共对象[]转换回(对象值,类型[]目标类型,对象参数,CultureInfo区域性)
{
抛出新的NotSupportedException();
}
}
并在XAML中使用它,如下所示:
public class ElementPermissionToVisibilityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
Dictionary<string, Action> permissions = values.OfType<Dictionary<string, Action>>().FirstOrDefault();
FrameworkElement element = values.OfType<FrameworkElement>().FirstOrDefault();
if (permissions != null && element != null && !string.IsNullOrWhiteSpace(element.Name))
{
Action action;
if (permissions.TryGetValue(element.Name, out action))
{
return action.CanBeShown ? Visibility.Visible : Visibility.Collapsed;
}
else
{
return Visibility.Collapsed;
}
}
else
{
return DependencyProperty.UnsetValue;
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
<MenuItem x:Name="systemMenuItem" Header="System">
<MenuItem.Visibility>
<MultiBinding Converter="{StaticResource ElementPermissionToVisibilityConverter}">
<Binding Path="Permissions"/>
<Binding RelativeSource="{RelativeSource Self}"/>
</MultiBinding>
</MenuItem.Visibility>
</MenuItem>
这将提供几个优点,其中最重要的是在权限属性更改时重新评估绑定
显然,这相当冗长,但您可以将绑定包装为MenuItem样式,因此只需定义一次。您可以定义如下多值转换器:
public class ElementPermissionToVisibilityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
Dictionary<string, Action> permissions = values.OfType<Dictionary<string, Action>>().FirstOrDefault();
FrameworkElement element = values.OfType<FrameworkElement>().FirstOrDefault();
if (permissions != null && element != null && !string.IsNullOrWhiteSpace(element.Name))
{
Action action;
if (permissions.TryGetValue(element.Name, out action))
{
return action.CanBeShown ? Visibility.Visible : Visibility.Collapsed;
}
else
{
return Visibility.Collapsed;
}
}
else
{
return DependencyProperty.UnsetValue;
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
<MenuItem x:Name="systemMenuItem" Header="System">
<MenuItem.Visibility>
<MultiBinding Converter="{StaticResource ElementPermissionToVisibilityConverter}">
<Binding Path="Permissions"/>
<Binding RelativeSource="{RelativeSource Self}"/>
</MultiBinding>
</MenuItem.Visibility>
</MenuItem>
公共类元素PermissionToVisibilityConverter:IMultiValueConverter
{
公共对象转换(对象[]值,类型targetType,对象参数,CultureInfo区域性)
{
字典权限=value.OfType().FirstOrDefault();
FrameworkElement元素=value.OfType().FirstOrDefault();
if(权限!=null&&element!=null&&string.IsNullOrWhiteSpace(element.Name))
{
行动;
if(permissions.TryGetValue(element.Name,out操作))
{
return action.CanBeShown?可见性。可见:可见性。折叠;
}
其他的
{
返回可见性。折叠;
}
}
其他的
{
返回dependencProperty.unset值;
}
}
公共对象[]转换回(对象值,类型[]目标类型,对象参数,CultureInfo区域性)
{
抛出新的NotSupportedException();
}
}
并在XAML中使用它,如下所示:
public class ElementPermissionToVisibilityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
Dictionary<string, Action> permissions = values.OfType<Dictionary<string, Action>>().FirstOrDefault();
FrameworkElement element = values.OfType<FrameworkElement>().FirstOrDefault();
if (permissions != null && element != null && !string.IsNullOrWhiteSpace(element.Name))
{
Action action;
if (permissions.TryGetValue(element.Name, out action))
{
return action.CanBeShown ? Visibility.Visible : Visibility.Collapsed;
}
else
{
return Visibility.Collapsed;
}
}
else
{
return DependencyProperty.UnsetValue;
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
<MenuItem x:Name="systemMenuItem" Header="System">
<MenuItem.Visibility>
<MultiBinding Converter="{StaticResource ElementPermissionToVisibilityConverter}">
<Binding Path="Permissions"/>
<Binding RelativeSource="{RelativeSource Self}"/>
</MultiBinding>
</MenuItem.Visibility>
</MenuItem>
这将提供几个优点,其中最重要的是在权限属性更改时重新评估绑定
显然,这相当冗长,但您可以将绑定包装为MenuItem样式,因此只需定义一次。一般来说,使用字典进行绑定是个坏主意,这可能就是MS从未创建可观察字典作为.Net framework的一部分的原因,您最好创建一个权限类,然后创建一个可观察的权限类集合,这将为您提供权限的集合绑定和更改绑定 注意:这使用C#6,因此如果您使用的是早期版本,则可能需要对其进行调整 范例 Xaml
由于使用字典进行绑定的一般规则是一个坏主意,这可能就是MS从未将可观察字典创建为.Net framework的一部分的原因,您最好创建一个权限类,然后创建它们的可观察集合,这将为您提供集合绑定和权限上的更改绑定 注意:这使用C#6,因此如果您使用的是早期版本,则可能需要对其进行调整 范例 Xaml
首先,我想对所有帮助我找到解决方案的人说声谢谢。 我创建了
SilentDictionary
类,并使用它来代替Dictionary
:
public class SilentDictionary<TKey, TValue> : IDictionary<TKey, TValue>
where TValue : class, new()
{
protected IDictionary<TKey, TValue> Dictionary;
public SilentDictionary()
{
Dictionary = new Dictionary<TKey, TValue>();
}
public TValue this[TKey key]
{
get
{
TValue item;
if (Dictionary.TryGetValue(key, out item))
return item;
else
{
item = new TValue();
Add(key, item);
return item;
}
}
set
{
Add(key, value);
}
}
public int Count
{
get
{
return Dictionary.Count;
}
}
public bool IsReadOnly
{
get
{
return Dictionary.IsReadOnly;
}
}
public ICollection<TKey> Keys
{
get
{
return Dictionary.Keys;
}
}
public ICollection<TValue> Values
{
get
{
return Dictionary.Values;
}
}
public void Add(KeyValuePair<TKey, TValue> item)
{
Add(item.Key, item.Value);
}
public void Add(TKey key, TValue value)
{
TValue item;
if (Dictionary.TryGetValue(key, out item))
{
Type t = typeof(TValue);
var props = t.GetProperties();
foreach (var p in props)
p.SetValue(item, p.GetValue(value));
}
else
Dictionary.Add(key, value);
}
public void Clear()
{
Dictionary.Clear();
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
return Dictionary.Contains(item);
}
public bool ContainsKey(TKey key)
{
return Dictionary.ContainsKey(key);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
Dictionary.CopyTo(array, arrayIndex);
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return Dictionary.GetEnumerator();
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
return Dictionary.Remove(item);
}
public bool Remove(TKey key)
{
return Dictionary.Remove(key);
}
public bool TryGetValue(TKey key, out TValue value)
{
return Dictionary.TryGetValue(key, out value);
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)Dictionary).GetEnumerator();
}
}
现在绑定没有DependencyProperty.UnsetValue
。绑定与我的静态SilentDictionary
实例有关联。下一步是对dictionary进行静态notify属性更改:
public abstract class ViewModelBase
{
public static Dictionary<string, Action> Permissions { get; set; }
...
}
public abstract class ViewModelBase
{
protected static SilentDictionary<string, Action> _permissions;
public static SilentDictionary<string, Action> Permissions
{
get { return _permissions; }
set
{
_permissions = value;
NotifyPropertyChanged();
}
}
public static event EventHandler PermissionsChanged;
protected static void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
EventHandler temp = Volatile.Read(ref PermissionsChanged);
PermissionsChanged?.Invoke(null, new EventArgs());
}
// other code
}
公共抽象类ViewModelBase
{
受保护的静态SilentDictionary\u权限;
公共静态SilentDictionary权限
{
获取{return\u permissions;}
设置
{
_权限=值;
NotifyPropertyChanged();
}
}
公共静态事件处理程序权限更改;
受保护的静态void NotifyPropertyChanged([CallerMemberName]字符串propertyName=”“)
{
EventHandler temp=Volatile.Read(参考权限更改);
PermissionsChanged?.Invoke(null,new EventArgs());
}
//其他代码
}
最后,我可以按照开始时的计划绑定到这本词典:
<MenuItem x:Name="systemMenuItem" Header="Система" Visibility="{Binding Permissions[systemMenuItem].CanBeShown, Converter={StaticResource BoolToVis}}">
而且一切正常。首先