如何将视图模型方法的结果数据绑定到文本框属性?

6
在我的视图模型和模型中,我有一个方法,签名为bool IsPropertyReadOnly(string propertyName)。该方法确定当前登录的用户是否可以编辑属性值。少数用户将能够编辑属性值,而大多数其他用户将只能读取。
我想要的是,不创建每个模型属性的只读状态返回属性,而是将IsPropertyReadOny的结果绑定到TextBox.IsReadOnly属性。
这是我设想的语法:
<TextBox Text="{Binding Address, Mode=TwoWay}" 
         IsReadOnly="{Binding MethodName=IsPropertyReadOnly MethodParameter=Address}"
/>
DataContext 包含了视图模型,所以基本上我需要将 IsReadOnly 绑定到调用 ((Class)this.DataContext).IsPropertyReadOnly("Address") 的结果。
虽然有很多使用 ObjectDataProvider 的文档,但是对象数据提供程序会创建一个新的对象实例,这不是我想要的。此外,要使用现有实例,我必须在代码后台进行赋值。同样,这也不是我想做的事情。
根据我的研究,似乎从 BindingMarkupExtension 继承的解决方案更适合我的需求。
非常感谢您的帮助。

这里可能有你问题的答案(最后一个使用转换器的答案): WPF中绑定到方法? - Anatolii Gabuza
3个回答

4

我建议使用一个转换器。以下是示例。假设您有一个简单的ViewModel类:

class ViewModel
{
    public string Read
    { get; set; }

    public string ReadWrite
    { get; set; }

    public bool IsPropertyReadOnly(string propertyName)
    {
        return propertyName != "ReadWrite";
    }
}

为了解决您的问题,您需要编写一个转换器,例如:
public class Converter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var vm = value as ViewModel;
        var functionName = (string)parameter;

        var result = vm.IsPropertyReadOnly(functionName);
        return result;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException("This method should never be called");
    }
}

这就是全部内容了;现在你可以在XAML中使用这个转换器,比如:

<Window.Resources>
    <temp:Converter x:Key="ReadOnlyMethodConverter"/>
</Window.Resources>
<StackPanel>
    <TextBox Text="{Binding Read, Mode=TwoWay}" 
             IsReadOnly="{Binding Path=.,
        Converter={StaticResource ReadOnlyMethodConverter}, ConverterParameter=Read}"
    />
    <TextBox Text="{Binding ReadWrite, Mode=TwoWay}" 
             IsReadOnly="{Binding Path=.,
        Converter={StaticResource ReadOnlyMethodConverter}, ConverterParameter=ReadWrite}"
    />
</StackPanel>

在代码后台,我们只需创建ViewModel并将其设置为DataContext:
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new ViewModel();
    }
}

尽管我在另一个答案中使用了H.B.的建议,但我还是接受并投票支持了这个答案,因为我认为它完全回答了关于绑定视图模型方法参数的问题。 - AMissico
3
我认为之前的评论中回答太多了。:O) - AMissico

4
此外,要使用现有实例,我必须在代码后端进行赋值。这并不是我想做的事情。
但这并不完全正确,只是你的选择会受到限制。
那索引器呢?
private readonly Dictionary<string, bool> _PropertyReadOnlyDictionary = new Dictionary<string, bool>();
public Dictionary<string, bool> PropertyReadOnlyDictionary { get { return _PropertyReadOnlyDictionary; } }

<TextBox Text="{Binding Address, Mode=TwoWay}"
        IsReadOnly="{Binding PropertyReadOnlyDictionary[Address]}" />

如果您不想使用字典,当然可以将您的方法封装在一个新类中,该类还允许通过索引器进行访问。
private readonly PropertyIsReadOnlyResolver _PropertyIsReadOnlyResolver = new PropertyIsReadOnlyResolver();
public PropertyIsReadOnlyResolver PropertyIsReadOnlyResolver { get { return _PropertyIsReadOnlyResolver; } }

public class PropertyIsReadOnlyResolver
{
    public bool this[string propertyName]
    {
        get
        {
            return IsPropertyReadOnly(propertyName);
        }
    }

    public bool IsPropertyReadOnly(string propertyName)
    {
        //...
    }
}

<TextBox Text="{Binding Address, Mode=TwoWay}"
        IsReadOnly="{Binding PropertyIsReadOnlyResolver[Address]}" />

有趣的是,我们一开始确实使用了索引器。但我最终决定不这样做,因为我们需要使用 IDataErrorInfo。我喜欢字典中的第一个索引器,因为它更适合我们的实现。 - AMissico
我们使用了PropertyReadOnlyDictionary实现,它与我们的设计完美地集成在一起。非常感谢。 - AMissico

0
你可以通过使用 ObjectDataProvider 来执行方法,然后将附加属性绑定到提供程序的返回值来完成此操作。
首先,您需要将提供程序配置为资源:
<Window.Resources>
  <ObjectDataProvider x:Key="readOnlyProvider" ...>
    <ObjectDataProvider.MethodParameters>
      ...
    </ObjectDataProvider.MethodParameters>
  </ObjectDataProvider>
</Window.Resources>

然后将提供程序用作附加属性绑定的源:

<TextBox Text="{Binding PoolNum, Mode=OneWay}" Windows:AttachedProperties.IsReadOnlyOn="{Binding Source={StaticResource readOnlyProvider}}" />

这个过程中比较棘手的部分是如何将一个值“传递”给ObjectDataProviders.MethodParameters。这可以在XAML中完成,有很多资源可以向你展示如何完成;这里有一个入门教程:http://weblogs.asp.net/psheriff/archive/2010/02/23/bind-objectdataprovider-method-parameters-in-wpf.aspx

更新

根据您的评论,这里有两种方法可以让ObjectDataProvider在不创建新对象的情况下执行视图的DataContext上的方法。

首先,将您的视图模型方法设置为静态,并使用ObjectType属性:

<ObjectDataProvider x:Key="readOnlyProvider"
  ObjectType="{x:local MyDataContext}"
  MethodName="IsPropertyReadOnly">
  ...
</ObjectDataProvider>

或者,在视图加载时将提供程序的ObjectInstance设置为视图的DataContext

public class MyWindow : Window
{
  public MyWindow()
  {
    InitializeComponent();

    var readOnlyProvider = this.Resources["readOnlyProvider"] as ObjectDataProvider;
    readOnlyProvider.ObjectInstance = this.DataContext;
  }
}

在 XAML 中没有办法绑定到方法,我所知道的唯一解决方法是使用 ObjectDataProvider


但是,我不想让ObjectDataProvider创建一个新对象。如果我可以设置提供程序的对象,那就太好了。 - AMissico

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接