从自定义窗口样式绑定到ViewModel

时间:2015-02-08 10:24:01

标签: c# wpf xaml mvvm data-binding

如果我告诉我,我可能会以错误的方式解决这个问题。

我正在构建一个可重复使用的自定义窗口样式,我已经重新创建了标题栏和其他所有内容。现在我想在标题栏中放置登录数据(用户名,图片和登录/注销)。

我已经拥有了Xaml设计的一切,看起来很棒。我也有一个DependecyProperty,我可以使用这种风格从我的窗口设置,我希望登录área通过扩展类显示或不显示。

现在我需要创建DependencyProperties来保存用户数据并将其绑定到窗口样式定义中,但是,如果我在样式中设置它们并将属性放在标题栏自定义控件的代码后面我创建(所以我可以处理拖动等)然后我无法从viewmodel访问它们来设置/获取它们(或者至少我找不到方法) - 但如果我没有设置他们的风格那么我将不会知道如何设置它们。

那么我怎么能: 1 - 从我的DependencyProperties访问视图的ViewModel(除了可能要求将view传递给viewmodel - 这似乎打败了MVVM的目的} 2 - 从我的窗口DependencyProperties绑定此样式的Xaml - 似乎是更好的选择,但不确定如何去做它

编辑:似乎需要进一步澄清:

我正在构建具有窗口样式(以及其他内容)的库(dll),以便在多个项目中可重用。 作为上面窗口样式的一部分,我有自己的自定义控件处理标题栏的东西(图标,标题,系统按钮,自定义系统按钮,窗口拖动,窗口调整大小和登录área)。一切正常,但登录área。

登录área基本上是:

     user name here |  PIC |
   Sign in/out here | HERE |

这与我的自定义标题栏重叠:

   [icon] App Name        [LoginArea][Custom Buttons][System Buttons]

我的问题是 - 如何将登录的用户信息绑定到此登录区域?

2 个答案:

答案 0 :(得分:1)

这是最简单的方法,你可以使用一个小的缩回。分配给此窗口的任何视图模型都必须包含Login对象。

主要应用程序中的MainWindow:

<Window Style="{StaticResource myWindow}">
</Window>

MainWindow代码背后:(这是设置视图模型的地方)

public MainWindow()
{
    InitializeComponent();
    var vm = new MainVm();
    vm.Login = new LoginVm();
    vm.Login.Username = "please enter username";
    DataContext = vm;
}

查看模型:( MainVm包含LoginVm的实例)

public class MainVm : DependencyObject
{
    /// <summary>
    /// Gets or sets a bindable value that indicates Login
    /// </summary>
    public LoginVm Login
    {
        get { return (LoginVm)GetValue(LoginProperty); }
        set { SetValue(LoginProperty, value); }
    }
    public static readonly DependencyProperty LoginProperty =
        DependencyProperty.Register("Login", typeof(LoginVm), typeof(MainVm), 
        new PropertyMetadata(null));
}
public class LoginVm : DependencyObject
{
    /// <summary>
    /// Gets or sets a bindable value that indicates Username
    /// </summary>
    public string Username
    {
        get { return (string)GetValue(UsernameProperty); }
        set { SetValue(UsernameProperty, value); }
    }
    public static readonly DependencyProperty UsernameProperty =
        DependencyProperty.Register("Username", typeof(string), typeof(LoginVm), 
        new PropertyMetadata(""));
}

库中的TitleBar CustomControl :(添加了一个属性)

public class TitleBar : Control
{
    static TitleBar()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(TitleBar), new FrameworkPropertyMetadata(typeof(TitleBar)));
    }
    /// <summary>
    /// Gets or sets a bindable value that indicates Username
    /// </summary>
    public string Username
    {
        get { return (string)GetValue(UsernameProperty); }
        set { SetValue(UsernameProperty, value); }
    }
    public static readonly DependencyProperty UsernameProperty =
        DependencyProperty.Register("Username", typeof(string), typeof(TitleBar), new PropertyMetadata("default"));
}

库中的资源字典:(注意绑定路径)

<Style TargetType="Window" x:Key="myWindow">
    <Setter Property="Background" Value="WhiteSmoke"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate>
                <StackPanel>
                    <ToolBar>
                        <local:TitleBar DataContext="{Binding Login}"/>
                    </ToolBar>
                </StackPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
<Style TargetType="{x:Type local:TitleBar}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:TitleBar}">
                <TextBox Text="{Binding Username}"/>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

如果这是您想要的,那么您可以以相同的方式添加其他属性和命令。我希望它有所帮助。

答案 1 :(得分:0)

所以我找到了解决方案。鉴于我的概念,我没有在库设计时绑定到的datacontext路径。每个使用此库的应用程序都会有自己的路径 - 因为该数据需要由应用程序而不是库来管理。但是,我可以在库中定义一个类供应用程序使用。

因此,在我的库中,我有一个用户数据类,可视化的最低要求加上可选用的字典,以保留应用程序可能需要的其他用户数据:

class UserData : INotifyPropertyChanged {
    .... //set private fields

    public string DisplayName {
        get { return ....; }
        set {
            if (.... != value) {
                .... = value;
                OnPropertyChanged();
            }
        }
    }

    .... Other properties including an all purpose dictionary to keep custom data
}

有了这个,我就可以在我的主窗口ViewModel中找到一个UserData类型的对象,该应用可以用来保存用户数据。

同时我的窗口样式使用我的自定义标题栏控件:

<Style x:Key="WindowStyle" TargetType="{x:Type Window}">
    .... <!-- other settings -->
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Window}">
                .... <!-- other stuff -->
                <controls:TitleBar x:Name="TitleBar" ..... />
                .... <!-- other stuff -->
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</style>

我的自定义标题栏的样式有我的登录área,所有控件都根据我为用户数据创建的类正确绑定:

.... <!-- other stuff -->
<TextBlock Text="{Binding Path=DisplayName}" />
.... <!-- other stuff -->

回到我的主窗口(即使用我的窗口样式引入所有这些)我将以下内容放在Loaded事件中(这似乎越早完成 - 如果我尝试这个,我会得到例外在构造函数中)

private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
    var titlebar = (Control)Template.FindName("TitleBar", this);
    titlebar.DataContext = (DataContext as MyMainViewModelType).LoggedUserData;
}

MyMainViewmodelType是我的主窗口ViewModel的类,LoggedUserData是该用户数据类型的Viewmodel中的一个属性(我的类从头开始),带有用户数据。

UserData类应该默认为所有属性,以便在登录之前显示的内容没有问题