绑定到WPF中的另一个绑定



在一个简单的MVVM应用程序中,我可以向视图中的某些属性添加绑定。属性可以访问另一个对象并从中返回属性值。假设我想在视图中显示活动项目。如果没有项目处于活动状态,将显示一个特殊的注释。

现在,当重命名项目时,视图中的名称应该更新。如果我只是在属性中返回项目的名称,它当然不会被更新。

所以我认为我可以将视图绑定到属性中创建的另一个绑定,它应该转发PropertyChanged事件并相应地更新视图。但我看到的是"System.Windows.Data"。而不是预期的绑定结果,如"Project: XYZ"。

该项目可以在任何地方重命名,所以我想避免从那里自己为这个ViewModel引发PropertyChanged事件。事情本身应该更聪明一点,而不需要从任何地方推来推去(当事情变得更复杂时,你经常至少忘记一次)。

代码如下:

XAML视图:

<TextBlock Text="{Binding ActiveProjectName}"/>
c# ViewModel:

public object ActiveProjectName
{
    get
    {
        if (ActiveProject != null)
        {
            // This works but won't update automatically:
            //return "Project: " + ActiveProject.Name;
            // This does not work at all:
            return new Binding("Name")
            {
                Source = ActiveProject,
                StringFormat = "Project: {0}"
            };
        }
        return "(No active project)";
    }
}

这是可能的吗?它是如何工作的?

除非ActiveProject属性是私有或受保护的,否则在xaml中使用FallbackValue而不是

后面代码中的if(ActiveProject != null)

例子
<TextBlock Text="{Binding ActiveProject.Name,StringFormat=Project: {0},FallbackValue=(No active project)}"/>

使用PriorityBinding具有条件绑定

<TextBlock>
    <TextBlock.Text>
        <PriorityBinding FallbackValue="(No active project)">
            <Binding Path="ActiveProject.Name"
                     StringFormat="Project: {0}"/>
            <Binding Path="SomeOtherProject.Name"
                     StringFormat="Other Project: {0}" />
        </PriorityBinding>
    </TextBlock.Text>
</TextBlock>

在上面的示例中PriorityBinding将首先尝试绑定到ActiveProject,然后使用Name属性来解析值。如果这是不可用的ie。null,那么它将尝试绑定到SomeOtherProject。为了根据绑定解析值,如果结果也是null,那么FallbackValue将被用作TextBlock的Text属性的值。

不不不不!; D

使用IValueConverter !http://msdn.microsoft.com/en-us/library/system.windows.data.ivalueconverter (v = vs.110) . aspx

通常我会保留一个ActiveProject/ActiveWhatever属性,我将在代码的某个地方设置,而不是创建越来越疯狂的绑定,使你的标记和代码变得复杂

还要确保在所有实体上实现INotifyPropertyChanged,这里你只有一个getter。在代码中设置绑定是你应该避免的事情!(DO vm's和DP's在其中是最重要的)。

数据模型/实体:

public class ActiveProject : INotifyPropertyChanged
{
    private string name;
    public String Name
    {
        get { return name; }
        set
        {
            if (value == name) return;
            name = value;
            OnPropertyChanged(); // Signals the UI that the property value has changed, and forces a re-evaluation of all bindings for this property
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    [NotifyPropertyChangedInvocator] // No R#? Shame on you and comment this line out
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }        
}

转换器:

public class YourConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        // Do whatever you want here
        ActiveProject project = value as ActiveProject;
        return project == null ? project.Name : "Whatever";
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        // Convert back if needed
        throw new NotImplementedException();
    }
}
XAML:

...
<xx.Resources>
    <local:YourConverter x:Key="YourConverter"/> <!--Define your converter as a staticresource, given that local is the namespace you have define. Alt+Enter with R# -->
<xx.Resources>
 ...
 <TextBlock Text="{Binding ActiveProject, Converter={StaticResource local:YourConverter}}"/>

最新更新