【问题标题】:Get or Set Property Values of INotifyPropertyChanged Class from Code Behind a Page Bound in XAML从 XAML 中页面绑定后面的代码获取或设置 INotifyPropertyChanged 类的属性值
【发布时间】:2014-09-02 15:07:14
【问题描述】:

我已将我的组框绑定到 INotifyPropertyChanged 类。

页面资源

    <Page.Resources>
       <current:UserAccountsStatusHandler x:Key="UserAccounts" />
    </Page.Resources>

组框

<GroupBox
                Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="2"
                Header="Select Action:"
                Foreground="{DynamicResource DynamicFrmFG}"
                VerticalAlignment="Stretch" HorizontalAlignment="Left"
                Height="50"
                DataContext="{StaticResource ResourceKey=UserAccounts}">

                <StackPanel 
                    Orientation="Horizontal"
                    HorizontalAlignment="Stretch" VerticalAlignment="Center"
                    Height="Auto">
                    <RadioButton
                        Content="Insert" 
                        Foreground="{DynamicResource DynamicFrmFG}" Height="16"
                        IsChecked="{Binding Path=UserAccountAction, Converter={StaticResource enumToBooleanConverter}, 
                                  ConverterParameter={x:Static enums:UserAccountActions.Insert}, UpdateSourceTrigger=PropertyChanged}"
                        Margin="0,0,10,0" 
                        Click="RadioButton_Click" />
                    <RadioButton
                        Content="Update" 
                        Foreground="{DynamicResource DynamicFrmFG}" Height="16"
                        IsChecked="{Binding Path=UserAccountAction, Converter={StaticResource enumToBooleanConverter}, 
                                  ConverterParameter={x:Static enums:UserAccountActions.Update}, UpdateSourceTrigger=PropertyChanged}"
                        Margin="0,0,10,0" 
                        Click="RadioButton_Click" />
                    <RadioButton
                        Content="Delete" 
                        Foreground="{DynamicResource DynamicFrmFG}" Height="16"
                        IsChecked="{Binding Path=UserAccountAction, Converter={StaticResource enumToBooleanConverter}, 
                                  ConverterParameter={x:Static enums:UserAccountActions.Delete}, UpdateSourceTrigger=PropertyChanged}"
                        Margin="0,0,10,0" 
                        Click="RadioButton_Click" />
                </StackPanel>
            </GroupBox>

public class UserAccountsStatusHandler : INotifyPropertyChanged
{
    private UserAccountActions userAccountAction;
    public UserAccountActions UserAccountAction 
    {
        get { return userAccountAction; } 
        set 
        {
            userAccountAction = value;
            IsSaveEnabled = (userAccountAction == UserAccountActions.None) ? false : true;
            OnPropertyChanged("UserAccountAction"); 
        } 
    }

    private bool isSavedEnabled;
    public bool IsSaveEnabled { get { return isSavedEnabled; } set { isSavedEnabled = value; OnPropertyChanged("IsSaveEnabled"); } }

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

这一切都按预期工作。我单击其中一个单选按钮,属性更改,保存按钮变为启用状态。但是,我试图弄清楚如何在页面后面的代码中从类中提取这些值,而实际上不必从 UI 中提取组框的 DataContext,然后调用类。

如果我尝试在页面后面的代码中创建一个类的新实例,我会得到默认值。到目前为止,我发现如何获取/设置值的唯一方法是将变量的值设置为等于 groupboxes 数据上下文,如下所示:

        var test = (UserAccountsStatusHandler)tempGroupBoxName.DataContext;
        test.IsSaveEnabled = false;

我读过的很多东西都表明数据层不应该对 UI 有任何了解。所以我不知道如何做到这一点。任何帮助将不胜感激。

编辑:添加我以前做的,我认为也是错误的。

private UserAccountsStatusHandler mainStatusHandler;
mainStatusHandler = new UserAccountsStatusHandler();               
base.DataContext = mainStatusHandler;

此时我可以很容易地调用 mainStatusHandler 来获取类似IsSavedEnabled 的内容,并确定用户从UserAccountAction 中选择的操作。我使用IsSavedEnabled 的唯一真正原因是在选项卡重新加载页面时禁用按钮。为了确保他们在没有实际选择操作之前不会点击保存按钮,然后启用保存按钮。然后他们必须实际点击“保存”按钮来执行后面的代码以将数据保存到服务器。

【问题讨论】:

  • 根据目前提供的答案,我可能需要阅读更多关于 MVVM 的信息。我可能没有像我想的那样掌握它。
  • 我为你更新了我的答案,提供了一个更好的例子来满足你的需求。从您的编辑中,很明显您只需要对 MVVM 和 WPF 进行一些复习。你不应该把这种东西放在你的代码隐藏中,它确实属于视图模型。

标签: c# wpf xaml binding datacontext


【解决方案1】:

由于您只是将其用于某种形式的验证,因此您确实需要使用 ICommand 实现。因此,在这种情况下,修改您的视图模型以在您的 INotifyPropertyChanged 旁边实现 ICommand 接口。

public class UserAccountsStatusHandler : INotifyPropertyChanged, ICommand
{
    private UserAccountActions userAccountAction;
    public UserAccountActions UserAccountAction
    {
        get { return userAccountAction; }
        set
        {
            userAccountAction = value;
            IsSaveEnabled = (userAccountAction == UserAccountActions.None) ? false : true;
            OnPropertyChanged("UserAccountAction");

            // Refresh the CanExecute method.
            OnCanExecuteChanged();
        }
    }

    private bool isSavedEnabled;
    public bool IsSaveEnabled { get { return isSavedEnabled; } set { isSavedEnabled = value; OnPropertyChanged("IsSaveEnabled"); } }

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    public bool CanExecute(object parameter)
    {
        return userAccountAction.SomeActionSelected != null;
    }

    public event EventHandler CanExecuteChanged;
    private void OnCanExecuteChanged()
    {
        if (this.CanExecuteChanged!= null)
            this.CanExecuteChanged(this, new EventArgs());
    }

    public void Execute(object parameter)
    {
        // perform save code.
    }
}

现在在您的 XAML 中,如果您的 groupbox 有一个 UserAccountStatusHandler 作为其数据上下文,您可以使用为您的 Groupbox 分配一个名称,并将您的按钮绑定到它。

<GroupBox x:Name="UserAccountStatusGroupBox"></GroupBox>

<Button Content="Save"
        Command={Binding ElementName=UserAccountStatusGroupBox, Path=DataContext} />

加载视图时,按钮调用 CanExecute。如果返回值为 false,则按钮自动禁用。如果为真,则启用。当您的视图模型中的属性发生更改时,您将调用CanExecuteChanged 处理程序,它将重新评估是否可以启用保存按钮。

一般来说,每个视图/用户控件应该有一个视图模型。在这种情况下,您的视图(窗口/页面)应该将视图模型作为其数据上下文,而不是 GroupBox。这是最佳做法。

我向您推荐 read up on the Command Pattern,MVVM 可以与之配合使用。对于这样的事情,根本应该有零代码隐藏。它应该全部包含在视图模型中。 View Model 的目的是支持 View。

【讨论】:

  • 非常感谢。你所展示的内容对我来说确实更有意义,我肯定会在本周末阅读更多关于 MVVM 和你推荐的链接的内容。
  • 阅读该模式是个好主意,然后探索它的一些可用于 WPF 的各种实现。 MVVMLight 和 Prism 一样是一个很好的实现(尽管 WPF 的 Prism 对于非企业\模块化应用程序来说有点过分了)。 WPF 的默认 MVVM 实现还有很多不足之处。
【解决方案2】:

一般来说,你创建的每个 View 都应该有一个 ViewModel:

<Page.DataContext>
     <local:MyViewModel/>
</Page.DataContext>

在这种情况下,您的 UserAccountsStatusHandler 可能是您的 ViewModel。但是通过在 Page 级别将其设置为 DataContext,您不必担心从后面的代码中挖掘 UI。

您可以像这样在 CodeBehind 中公开一个属性:

private MyViewModel ViewModel 
{
    get 
    { 
        return DataContext as MyViewModel;
    }
}

现在你可以使用:

MyViewModel.IsSaveEnabled = false;

哪个更好。

但是,我会避免在代码隐藏中添加太多逻辑。我希望您的按钮在 ViewModel 上调用 Save() 方法,然后让 ViewModel 相应地更新自身。

【讨论】:

    【解决方案3】:

    通常我不会在页面/窗口上设置控件的DataContext,而是设置根对象本身的数据上下文。我还添加了一个私有属性,以便在必要时使用(它只是将DataContext 的值返回给我的视图模型的类)。

    那么你只需要使用这个属性,它就可以让你访问绑定到 UI 的值。

    【讨论】:

    • 我认为您不需要从代码隐藏中访问您的视图模型。代码隐藏应该用于操作 View 的呈现方式,将数据提取留给 XAML 中的绑定。我曾经需要在我的代码隐藏中放置与视图模型相关的代码的唯一原因是使用通过非 DI 应用程序上的构造函数传入的依赖项来实例化它。理想情况下,您应该从您的 XAML 元素值本身 (var name = this.lblName.Text) 获取代码隐藏中所需的数据,从而为您提供来自 VM 和您的代码隐藏的抽象层。
    猜你喜欢
    • 2016-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-29
    • 2011-02-25
    • 2016-05-28
    • 2011-07-30
    • 1970-01-01
    相关资源
    最近更新 更多