WPF MVVM:如何在用户控件上设置绑定? [英] WPF MVVM: How to setup binding on user controls?

查看:116
本文介绍了WPF MVVM:如何在用户控件上设置绑定?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在理解绑定到用户控件的工作方式以及为什么它看起来与页面上的工作方式不同时遇到问题。我要尝试做的是创建一个错误显示(错误名称,描述和提示,告诉您如何进行故障排除),如果有错误,将从内容控件中显示,如果没有错误,则显示其他内容。

I'm having a problem grasping how binding to user controls works and why it seems to work differently than on pages. What I'm trying to do is create an error display (Error Name, Description and Tips, which tells how to troubleshoot it) that will show from a content control if there's an error, or other stuff if there's no error.

我正在使用用户控件执行此操作,该控件实际上将是页面上的子视图,以避免出现粗鲁的弹出窗口,并将在多个页面上重用。我有内容控件绑定在工作,所以我们只显示用户控件,没有任何信息。

I'm doing this with a user control that will essentially be a sub-view on a page to avoid having rude popups, and will be reused on multiple pages. I have the content control binding working so we get the user control displayed, just no info.

出于干燥的目的,我创建了一个具有所需属性的错误模型,然后使用一个实现此模型为错误列表的类。在构造函数中,我只需向列表中添加新的错误...这样,所有应用程序的错误都在同一位置,易于维护。

For 'DRY' purposes, I've created an Error Model with the desired properties and then use a class to implement this model as a list of errors. In the constructor, I simply add new errors to the list...this way all of the app's errors are in the same place for easy maintenance.

public List<ErrorMessageModel> errors;

/// <summary>
/// Constructor creates list with all errors in the program
/// </summary>
public SystemErrors()
{
            
    errors = new List<ErrorMessageModel>()
    {
        //*** No Error ***/
        new ErrorMessageModel(ErrorCodes.noError, "", "", ""),

        /*** No Devices Found Error ***/
        new ErrorMessageModel(ErrorCodes.noDevicesConnected,
                              "No Devices Found",
                              "We couldn't find any attached USB devices.",
                              "This error occurs when there's no connection between the device and the computer ")

        /*** Next Error ***/
    };
}

private ErrorMessageModel _activeError;
public ErrorMessageModel ActiveError
{
    get { return _activeError; }
    set
    {
        if (value == _activeError)
            return;

        _activeError = value;
        RaisePropertyChanged();
    }
}

public void SetActiveError (byte index)
{
    // Changed to ActiveError = after Mark's answer. No effect.
    _activeError = errors[index];

}

在页面的视图模型中,我们使用枚举ErrorCodes来命名名称错误的索引。因此,当我们遇到错误时,我们将errorCode传递给将其强制转换为字节的方法,然后调用SetActiveError(字节errorCodeToIndex)。

In the page's view model, we use an enum ErrorCodes to have a name pointing to the index of the error. So when we have an error, we pass the errorCode to a method that casts it as a byte and then calls SetActiveError (byte errorCodeToIndex).

...
private void parseErrorCode(ErrorCodes error)
{
    // Convert Error Code into Index number
    var errorCodeToIndex = (byte)error;

    // Create new error list and populate list
    SystemErrors errors = new SystemErrors();

    errors.SetActiveError(errorCodeToIndex);
}

现在,这里的想法是将用户控件的数据上下文设置为SystemError,从而绑定到ActiveError( ActiveError.ErrorName,ActiveError.ErrorDescription等)。我的想法是,这将使我们可以使用单个数据上下文,因为无论发生错误时我们在哪个页面上,错误信息始终来自SystemErrors。

Now the idea here is to set the user control's data context to SystemError and thus bind to ActiveError (ActiveError.ErrorName, ActiveError.ErrorDescription, etc.). My thinking was this would allow us to use a single datacontext because no matter what page we are on when we have an error, the error info always comes from SystemErrors.

<UserControl x:Class="FirmwareUpdaterUI.Views.ConnectionErrorView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:FirmwareUpdaterUI.Views"
             xmlns:vm="clr-namespace:FirmwareUpdaterUI.ViewModels"
             xmlns:e="clr-namespace:FirmwareUpdaterUI.Errors"
             mc:Ignorable="d" 
             d:DesignHeight="250" d:DesignWidth="400" BorderBrush="Red" BorderThickness="1px">

    <UserControl.DataContext>
        <e:SystemErrors/>
    </UserControl.DataContext>

    <Grid x:Name="ConnectionErrorView" Visibility="Visible">
            <Grid.RowDefinitions>
                <RowDefinition Height=".5*"/>
                <RowDefinition Height="auto"/>
                <RowDefinition Height="auto"/>
                <RowDefinition Height="auto"/>
                <RowDefinition Height="6*"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="1.5*"/>
                <ColumnDefinition Width=".5*"/>
                <ColumnDefinition Width="10*"/>
                <ColumnDefinition Width="1*"/>
            </Grid.ColumnDefinitions>

            <!-- Row 1-->
            <StackPanel Grid.Row="1" Grid.Column="2" Orientation="Horizontal">
                <TextBlock>
                    Error:
                </TextBlock>
                <TextBlock Text="{Binding ActiveError.ErrorName, 
                           RelativeSource={RelativeSource AncestorType={x:Type e:SystemErrors}}}"/>
            </StackPanel>

            <!-- Row 2 -->
            <TextBlock Grid.Row="2" Grid.Column="2" Grid.ColumnSpan="2"
                       Text="{Binding ErrorDescription}"/>

            <!-- Row 3 -->
            <TextBlock Grid.Row="3" Grid.Column="2" Grid.RowSpan="2" Grid.ColumnSpan="2" 
                       Text="{Binding Path=ActiveError.ErrorTips, StringFormat=Tips: {0}}" />
        </Grid>

</UserControl>

但是,我似乎无法正常工作。您可以在XAML中看到我所有剩余的失败方法,但这只是我尝试过的内容的一部分。如果我剪掉UC的内胆并将其粘贴到页面中,则可以使它正常工作,因此告诉我绑定到页面的机制与用户控件的机制不同。

But, I can't seem to get it to work. You can see all of my leftover failed approaches in the XAML, but this only scratches the surface of what I've tried. I can get this to work if I cut the guts of the UC out and paste it in the page, so what that tells me is binding to a page has a different mechanism than to a user control.

我看了很多教程,看了一些视频,但是所有这些都略过了它的工作原理。它始终是为了使这项工作有效,我们需要这个已经有效的代码。仅当您遇到完全相同的问题时,这才有帮助。我已经看到了依赖项属性,似乎是普通绑定,相对于自身的相对源,相对于祖先的相对源等。

I've read a bunch of tutorials, watched a few videos, but all of them kind of skip over the how it works bit; its always "to make this work, we need this already working code" which only helps if you have the exact same problem. I've seen dependency properties, what appears to be normal binding, relative source to self, relative source to ancestor, etc.

那么,为什么用户控件似乎与Windows /页面具有不同的绑定机制(为什么数据上下文不能像其他地方那样工作)?如果我们需要依赖项属性,那么为什么不需要它们来绑定页面呢?而且,关于DP,如果需要的话,在这种情况下,我是否只需要创建一个ErrorModel类型的ActiveErrorProperty,还是为每个子属性(字符串类型的ErrorName)都需要一个ActiveErrorProperty?我们如何将DP链接到我们要绑定的属性上?

So why does a user control appear to have a different binding mechanism than windows/pages (why does the data context not work like it does elsewhere)? If we need dependency properties, then why don't we need them for binding to pages? And also in regards to DPs if needed, in this case, would I just make an ActiveErrorProperty of type ErrorModel, or do we need one for each sub-property (ErrorName of type string)? How do we link the DP to the property we want to bind to?

今天整天都试图使其正常工作,所以我开始跟踪并输出到控制台。都没有绑定错误,并且如果我在<$ c之后将 Trace.WriteLine 保留在 ActiveError 的公共声明中$ c> RaisePC(), ActiveError 将被设置为正确的错误。然后我尝试在XAML中跟踪绑定,并且有一些有趣的事情:

Tried all day today to get this working so I began tracing and outputting to the console. There were both no binding errors, and if I stuck Trace.WriteLine in the public declaration of ActiveError after RaisePC(), ActiveError would be set to the correct error. Then I tried tracing the binding in the XAML and there's some interesting things:

ErrorName(_activeError)= No Devices Found
ErrorName(ActiveError)= No Devices Found
System.Windows.Data Warning: 56 : Created BindingExpression (hash=62991470) for Binding (hash=23560597)
System.Windows.Data Warning: 58 :  Path: 'ActiveError.ErrorName'
System.Windows.Data Warning: 60 : BindingExpression (hash=62991470): Default mode resolved to OneWay
System.Windows.Data Warning: 62 : BindingExpression (hash=62991470): Attach to System.Windows.Controls.TextBlock.Text (hash=2617844)
System.Windows.Data Warning: 67 : BindingExpression (hash=62991470): Resolving source 
System.Windows.Data Warning: 70 : BindingExpression (hash=62991470): Found data context element: TextBlock (hash=2617844) (OK)
System.Windows.Data Warning: 78 : BindingExpression (hash=62991470): Activate with root item SystemErrors (hash=52209455)
System.Windows.Data Warning: 108 : BindingExpression (hash=62991470):   At level 0 - for SystemErrors.ActiveError found accessor RuntimePropertyInfo(ActiveError)
System.Windows.Data Warning: 104 : BindingExpression (hash=62991470): Replace item at level 0 with SystemErrors (hash=52209455), using accessor RuntimePropertyInfo(ActiveError)
System.Windows.Data Warning: 101 : BindingExpression (hash=62991470): GetValue at level 0 from SystemErrors (hash=52209455) using RuntimePropertyInfo(ActiveError): <null>
System.Windows.Data Warning: 106 : BindingExpression (hash=62991470):   Item at level 1 is null - no accessor
System.Windows.Data Warning: 80 : BindingExpression (hash=62991470): TransferValue - got raw value {DependencyProperty.UnsetValue}
System.Windows.Data Warning: 88 : BindingExpression (hash=62991470): TransferValue - using fallback/default value ''
System.Windows.Data Warning: 89 : BindingExpression (hash=62991470): TransferValue - using final value ''

请注意,它表明 ActiveError 设置正确(前两行,找不到设备是ErrorName),在 之前,我们看到绑定失败。我对WPF太陌生,但是如果我正确地解释了跟踪,它看起来好像在datacontext SystemErrors ActiveError c>,但无法从 ActiveError.ErrorName 获取任何信息,我们知道该值设置为正确的值。

Note that it shows that ActiveError is set correctly (first two lines, "No Devices Found" is the ErrorName) before we see the binding fail. I'm too new to WPF but if I'm interpreting the trace correctly it looks like it finds ActiveError in the datacontext SystemErrors but fails to get anything from ActiveError.ErrorName, which we know is set to the correct value. What's that about?

推荐答案

SystemErrors 不是它的视觉祖先。 UserControl 。它是 DataContext ,因此只要 ErrorMessageModel 类具有公共属性,以下内容就绑定而言应该起作用 ErrorName 属性,返回您期望返回的结果:

SystemErrors is not a visual ancestor of the UserControl. It is the DataContext so the following should work as far as the binding is concerned provided that the ErrorMessageModel class has a public ErrorName property that returns what you expect it to return:

<TextBlock Text="{Binding ActiveError.ErrorName}"/>

以下内容将设置为 ErrorMessageModel 属性并引发 PropertyChanged 事件:

The following will however not set the ErrorMessageModel property and raise the PropertyChanged event:

_activeError = errors[index];

您应将属性设置为新的 ErrorMessageModel 对象:

You should set the property to a new ErrorMessageModel object:

public void SetActiveError(byte index)
{
    ActiveError = errors[index];
}

还请确保调用 SetActiveError SystemErrors 类的实际实例上的code>方法:

Also make sure that you call the SetActiveError method on the actual instance of the SystemErrors class that you create in your XAML markup:

<UserControl.DataContext>
    <e:SystemErrors/>
</UserControl.DataContext>

这篇关于WPF MVVM:如何在用户控件上设置绑定?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆