C# 使用ViewModel中的多个变量绑定WPF控件可见性

C# 使用ViewModel中的多个变量绑定WPF控件可见性,c#,wpf,xaml,C#,Wpf,Xaml,我正在编写一个WPF用户控件,它显示一个动态生成的TabControl,其中包含多个页面,每个页面依次包含一个动态生成的控件列表(文本框、复选框等) 我想根据用户是否有查看权限来设置文本框、复选框控件的可见性。此权限是每个控件ViewModel(“VisiblyBy”)上的值和整个UserControl ViewModel(“UserRole”)的属性的组合。我刚刚开始使用WPF,但标准方法似乎是使用ValueConverter,但我不明白如何编写一个可以组合/访问不同属性(VisiblyBy和

我正在编写一个WPF用户控件,它显示一个动态生成的TabControl,其中包含多个页面,每个页面依次包含一个动态生成的控件列表(文本框、复选框等)

我想根据用户是否有查看权限来设置文本框、复选框控件的可见性。此权限是每个控件ViewModel(“VisiblyBy”)上的值和整个UserControl ViewModel(“UserRole”)的属性的组合。我刚刚开始使用WPF,但标准方法似乎是使用ValueConverter,但我不明白如何编写一个可以组合/访问不同属性(VisiblyBy和UserRole)的转换程序,因为它们存在于我的ViewModel层次结构的不同级别

以下是XAML的一部分,我在其中绑定到ViewModel:



您可以尝试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; 
}