【问题标题】:Bind to another Binding in WPF绑定到 WPF 中的另一个绑定
【发布时间】:2014-08-04 07:58:27
【问题描述】:

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

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

所以我想我可以将视图绑定到在属性中创建的另一个绑定,该绑定应该转发 PropertyChanged 事件并相应地更新视图。但我看到的是“System.Windows.Data.Binding”,而不是像“Project: XYZ”这样的绑定预期结果。

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

代码如下:

XAML 视图:

<TextBlock Text="{Binding ActiveProjectName}"/>

C# 视图模型:

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)";
    }
}

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

【问题讨论】:

    标签: c# wpf xaml mvvm data-binding


    【解决方案1】:

    除非 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 属性来解析值。如果那不可用,即。 null,那么它将尝试绑定到 SomeOtherProject。要根据绑定解析值,如果该结果也为 null,则 FallbackValue 将用作 TextBlock 的 Text 属性的值。

    【讨论】:

    • 好的,这可能适用于简单的情况。但在需要属性代码做出更多决策的情况下,情况可能会更加复杂。
    • 如果决策只是检查空值,您可能会使用优先级绑定。或者您可以在 ActiveProjectName 应该更新时直接触发 PropertyChanged
    • 所以你说不能在绑定的属性中返回另一个Binding?
    • 我的意思不是这样,但是一旦从 getter 返回绑定,它将始终用于提供值。在这种情况下,不会再次调用 getter,并且不会评估条件 if (ActiveProject != null)。所以它不会满足你的期望。它也适用于 getter 的其他部分,它返回一个字符串实例。
    • 我知道 VM 属性只读取一次(然后在通知更改时)。但 XAML 对象的(FrameworkElement 或 UIElement)属性也不包含数据本身,而是包含一个 Binding 实例。该绑定解析数据(一次)。它可以手动更新,但这也很丑陋。所以我想我可以添加第二级间接,让第一个 Binding 指向另一个可以通知实际属性更改并自行触发更新的绑定。
    【解决方案2】:

    不不不不不! ;D

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

    通常我会保留一个 ActiveProject/ActiveWhatever 属性,我会在代码中的某个位置设置该属性,而不是创建更疯狂和更疯狂的绑定,使您的标记和代码都复杂化

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

    数据模型/实体:

    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}}"/>
    

    【讨论】:

    • Project.Name 改变时,绑定到Project 怎么知道这个?对 Name 属性的引用仅在 XAML 不可见的转换器代码中。 (只通知属性“名称”。但这不是绑定的目标。)虽然我理解你的想法,但我看不出答案如何解决我的问题。
    • @LonelyPixel 我的速度有点快,但你让这变得复杂了,复杂是不好的。正如您所说,ActiveProject 必须更改(IE 更改代码中的整个道具,正如我试图解释的那样,不是很好),而不是使您的代码混乱。现在它只是名称,后来它是别的东西......现在上面的解决方案适合你。但在一般(非经验,曾经做过)的基础上,你正在把自己挖到一个洞里。活动项目=项目A; ... ActiveProject = ProjectB.. 就像蛋糕一样简单!
    • 我的意思是不是整个 ActiveProject 实例被替换和重新分配,而是当前实例的一个属性(如名称)发生了变化。价值转换器永远不会知道这一点。当项目更改名称时,您的解决方案根本不会更新。
    猜你喜欢
    • 2013-02-05
    • 1970-01-01
    • 2018-01-13
    • 2011-01-06
    • 1970-01-01
    • 1970-01-01
    • 2011-08-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多