从自定义 window 样式绑定到 ViewModel

Binding from custom window Style into ViewModel

我可能会以错误的方式解决这个问题,所以如果我知道的话。

我正在构建一个可重用的自定义 window 样式,我已经在其中重新创建了标题栏和其他所有内容。现在我也想在标题栏中放置登录数据(用户名、图片和符号 in/out)。

我已经在我的 Xaml 中设计了所有的东西,看起来很棒。我还有一个 DependecyProperty,我可以从我的 window 中使用这种样式设置我是否希望通过扩展 class.

显示或不显示登录区域

现在我需要创建 DependencyProperties 来保留用户数据并将其绑定到 window 样式定义中,但是,如果我将它们设置在样式中并将属性放在后面的代码中对于我创建的标题栏自定义控件(因此我可以处理拖动等)然后我无法从 viewmodel 到 set/get 访问它们(或者至少我找不到方法) - 但如果我不按照样式设置它们,那么以后就没有办法设置它们了)

那么我怎么能: 1 - 从我的 ViewModel 访问视图的 DependencyProperties (除了可能需要将 view 传递给 viewmodel - 这似乎违背了 [=21= 的目的] 2 - 从我的 window 的 Xaml 中绑定此 DependencyProperties 样式 - 似乎是更好的选择,但不确定如何去做

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

我正在构建具有 window 风格(除其他外)的库 (dll),以便在多个项目中重复使用。 作为上面 window 样式的一部分,我有自己的自定义控件处理标题栏内容(图标、标题、系统按钮、自定义系统按钮、window 拖动、window 调整大小和登录区域) .一切正常,但登录区域。

登录区基本是:

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

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

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

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

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

所以在我的库中,我有一个 class 用于用户数据,其中包含可视化所需的最低限度以及一个可供选择使用的字典,以保留应用程序可能需要的其他用户数据用于其自身目的:

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
}

有了这个,我就可以在我的主 window 的 ViewModel 中拥有一个 object 类型的 UserData,应用程序可以使用它来保存用户数据。

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

<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>

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

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

回到我的主要 window(即使用我的 window 风格来引入所有这些)我将以下内容放在 Loaded 事件中(这似乎越快越好完成 - 如果我在构造函数中尝试这个,我会得到异常)

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

其中 MyMainViewmodelType 是我的主要 window 的 ViewModel 的 class,并且 LoggedUserData 是 属性 在该视图模型中的 属性使用用户数据键入 UserData(我的 class 从一开始)。

UserData class 应该实例化所有属性的默认值,以便在登录之前显示的内容没有问题

这是使它工作的最简单方法,但有一点小缺点。分配给此 window 的任何视图模型都必须包含登录对象。

主应用程序中的主窗口:

<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>

如果这是你想要的,那么你可以用同样的方式添加额外的属性和命令。希望对您有所帮助。