C# 使用ViewModel中的多个变量绑定WPF控件可见性
我正在编写一个WPF用户控件,它显示一个动态生成的TabControl,其中包含多个页面,每个页面依次包含一个动态生成的控件列表(文本框、复选框等) 我想根据用户是否有查看权限来设置文本框、复选框控件的可见性。此权限是每个控件ViewModel(“VisiblyBy”)上的值和整个UserControl ViewModel(“UserRole”)的属性的组合。我刚刚开始使用WPF,但标准方法似乎是使用ValueConverter,但我不明白如何编写一个可以组合/访问不同属性(VisiblyBy和UserRole)的转换程序,因为它们存在于我的ViewModel层次结构的不同级别 以下是XAML的一部分,我在其中绑定到ViewModel:C# 使用ViewModel中的多个变量绑定WPF控件可见性,c#,wpf,xaml,C#,Wpf,Xaml,我正在编写一个WPF用户控件,它显示一个动态生成的TabControl,其中包含多个页面,每个页面依次包含一个动态生成的控件列表(文本框、复选框等) 我想根据用户是否有查看权限来设置文本框、复选框控件的可见性。此权限是每个控件ViewModel(“VisiblyBy”)上的值和整个UserControl ViewModel(“UserRole”)的属性的组合。我刚刚开始使用WPF,但标准方法似乎是使用ValueConverter,但我不明白如何编写一个可以组合/访问不同属性(VisiblyBy和
您可以尝试IMultiValueConverter并使用多重绑定
<Window x:Class="ItemsControl_Learning.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ItemsControl_Learning"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:VisibilityConverter x:Key="conv" />
</Window.Resources>
<Grid>
<Button Content="Test">
<Button.Visibility>
<MultiBinding Converter="{StaticResource conv}">
<Binding Path="Role" />
<Binding Path="OtherProp" />
</MultiBinding>
</Button.Visibility>
</Button>
</Grid>
在Convert(…)
方法中,values
数组中不同输入的顺序与MultiBinding.Bindings
集合中的顺序相同
在本例中,
values[0]
包含Role
属性,而values[1]
将是OtherProp
,因为这是它们在XAML中插入的顺序我只需在ViewModel上创建一个名为IsAuthorized
的属性,并绑定到该属性。因为在调用您提到的其他属性并处理该授权逻辑以返回可见状态(不需要转换器)后,它将返回用户是否被授权
请注意,我们的角色
和IsOther
属性实际上也在我们的已授权的属性上调用属性更改
。当其中任何一项发生变化时,Xaml会得到适当的通知并相应地发生变化
人们似乎认为一个人只需要绑定到一个特定的属性,但是当简单地调用property并用复合属性进行更改就可以完成任务时,为什么要让你的生活更艰难呢 为了完整起见,这里是我最后使用的多值转换器-注意,我需要在对象数组上添加更好的错误检查功能,但这个想法是可行的
public class AccessLevelToVisibilityConverter : MarkupExtension, IMultiValueConverter
{
public object Convert( object[] values, Type targetType, object parameter, CultureInfo culture)
{
var visibility = Visibility.Hidden;
var viewModel = (VariableDataViewModel)values[0];
var item = (VariableDataItem)values[1];
visibility = viewModel.IsVariableVisible(item);
return visibility;
}
public object[] ConvertBack( object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
这是对应的XAML:
<Grid.Visibility>
<MultiBinding Converter="{p:AccessLevelToVisibilityConverter}" >
<Binding Path="DataContext",
RelativeSource="{RelativeSource
AncestorType=UserControl}" />
<Binding Path="." />
</MultiBinding>
</Grid.Visibility>
我需要将其应用于多个数据模板,因此我想实现这一点的方法是通过样式。如果我们将控制反转方法从转换器转移到实际对象来检查安全性/可见性,会怎么样
要做到这一点,我们需要让实体实例报告其可见性,而不需要转换器,但仍然使用VM在两部分系统中的IOC
依赖注入方法中提供授权
让我用代码解释一下:
使用访问级别的主题
public enum SecurityLevels
{
publicLevel = 0,
userLevel,
adminLevel
}
VM仍将包含当前安全级别(最终基于实际用户登录角色?)以及报告访问实例是否有权访问/显示当前级别的方法。这将在VM必须遵守命名的IAuthorize
的接口中定义
public interface IAuthorize
{
SecurityLevels CurrentLevel { get; set; }
bool GetAuthorization(IAmIAuthorized instance);
}
需要显示的每个实体的IAmIAuthorized
(Am-I-Authorized)接口是两部分安全性的第二部分。请注意依赖项注入函数DetermineAuthorizationFunc
,它最终将通过依赖项注入从VM提供:
public interface IAmIAuthorized
{
SecurityLevels Level { get; }
Visibility IsVisible { get; }
bool IsAuthorized { get; }
Func<IAmIAuthorized, bool> DetermineAuthorizationFunc { get; set; }
}
有了它,我们将IOC从转换方法移到了VM中
下面是在这种情况下OP的VariableDataItem
类的实际实现:
public class VariableDataItem : IAmIAuthorized
{
public string Name { get; set; }
public SecurityLevels Level { get; set; }
public Func<IAmIAuthorized, bool> DetermineAuthorizationFunc { get; set; }
public bool IsAuthorized
{
get { return DetermineAuthorizationFunc != null &&
DetermineAuthorizationFunc(this); }
}
public Visibility IsVisible
{
get { return IsAuthorized ? Visibility.Visible : Visibility.Hidden; }
}
}
公共类VariableDataItem:IAmIAuthorized
{
公共字符串名称{get;set;}
公共安全级别{get;set;}
公共函数DetermineAuthorizationFunc{get;set;}
公共图书馆被授权了
{
获取{return determinateAuthorizationfunc!=null&&
DetermineAuthorizationFunc(this);}
}
公众能见度是可见的
{
获取{return IsAuthorized?Visibility.Visible:Visibility.Hidden;}
}
}
使用来自VM的代码
Holdings = new List<VariableDataItem>()
{
new VariableDataItem() {Name = "Alpha", DetermineAuthorizationFunc = GetAuthorization, Level = SecurityLevels.publicLevel },
new VariableDataItem() {Name = "Beta", DetermineAuthorizationFunc = GetAuthorization, Level = SecurityLevels.userLevel },
new VariableDataItem() {Name = "Gamma", DetermineAuthorizationFunc = GetAuthorization, Level = SecurityLevels.adminLevel }
};
Holdings=新列表()
{
新变量DataItem(){Name=“Alpha”,DetermineAuthorizationFunc=GetAuthorization,Level=SecurityLevels.publicLevel},
新的VariableDataItem(){Name=“Beta”,DetermineAuthorizationFunc=GetAuthorization,Level=SecurityLevel.userLevel},
新的VariableDataItem(){Name=“Gamma”,DetermineAuthorizationFunc=GetAuthorization,Level=SecurityLevel.adminLevel}
};
以及IOC依赖注入方法:
public bool GetAuthorization(IAmIAuthorized instance)
{
return (int)instance.Level <= (int)CurrentLevel;
}
public bool GetAuthorization(IAmIAuthorized实例)
{
返回(整数)实例。你可以尝试IMultiValueConverter和使用Multibinding。经过几个小时的阅读和尝试其他解决方案后,我最终编写了一个多值转换器,迄今为止效果非常好-非常感谢。我最终给出了两个答案…但我确实明白了为什么这是一个伟大的解决方案。+1来自我。:-)谢谢。我尝试了它,但我可以不知道如何访问顶级ViewModel和绑定到控件的实际数据模型的属性。我正在尝试更改.contd…Tth“value”参数传递到我的Convert()的可见性方法绑定到我的ViewModel,所以我有一半的数据(甚至只是我需要的VisibleBy属性-但是我无法让它工作,而且我读了更多关于ConverterParameter的内容,这似乎并不容易。@我已经提供了第二个答案;对于您正在做的事情来说,这是一个好的答案吗…也许。至少您有选择。:-)
public class VariableDataItem : IAmIAuthorized
{
public string Name { get; set; }
public SecurityLevels Level { get; set; }
public Func<IAmIAuthorized, bool> DetermineAuthorizationFunc { get; set; }
public bool IsAuthorized
{
get { return DetermineAuthorizationFunc != null &&
DetermineAuthorizationFunc(this); }
}
public Visibility IsVisible
{
get { return IsAuthorized ? Visibility.Visible : Visibility.Hidden; }
}
}
Holdings = new List<VariableDataItem>()
{
new VariableDataItem() {Name = "Alpha", DetermineAuthorizationFunc = GetAuthorization, Level = SecurityLevels.publicLevel },
new VariableDataItem() {Name = "Beta", DetermineAuthorizationFunc = GetAuthorization, Level = SecurityLevels.userLevel },
new VariableDataItem() {Name = "Gamma", DetermineAuthorizationFunc = GetAuthorization, Level = SecurityLevels.adminLevel }
};
public bool GetAuthorization(IAmIAuthorized instance)
{
return (int)instance.Level <= (int)CurrentLevel;
}