简体   繁体   中英

How to properly style a WPF TextBox using Caliburn.Micro MVVM?

i am currenty doing a little bit of styling for a login user control (username, password, login button), using Caliburn.Micro MVVM.

This is my current progress: I created the user control, with its standard WPF controls (1 TextBox, 1 PasswordBox, 1 Button) and made sure that talking to the API and getting a response works without any customization. Currently, each control, the UserName TextBox and the PasswordBox have a TextBlock to there left, to indicate there function.

What i want to achieve: I want to get rid of these TextBlocks and instead have a default value inside the TextBox and PasswordBox to indicate the function of each control. As soon as the user starts typing in the TextBox and PasswordBox, these default values become invisible/go away and are being replaced by the users input.

What doesn't work: As soon as i apply the custom style to my TextBox control, NotifyOfPropertyChange(() => UserName) doesn't catch the changing property values any more, therefore NotifyOfPropertyChange(() => CanLogin) returns false and the login button stays inactive sad face

i guess the reason for this must be the theme/style itself, but after a couple of hours wrestling with it, i can't figure out how to fix it... so any help from a Caliburn.Micro MVVM Guru, pointing in the right direction would be highly appreciated

The App is build in .NET Framework 4.8. I am using Caliburn.Micro 4.0.210.

The LoginViewModel:

public class LoginViewModel : Screen
{
    private string _userName;
    private string _password;
    private IAPIHelper _apiHelper;

    public LoginViewModel(IAPIHelper apiHelper)
    {
        _apiHelper = apiHelper;
    }

    public string UserName
    {
        get { return _userName; }
        set 
        { 
            _userName = value;
            NotifyOfPropertyChange(() => UserName);
            NotifyOfPropertyChange(() => CanLogin);
        }
    }

    public string Password
    {
        get { return _password; }
        set 
        { 
            _password = value;
            NotifyOfPropertyChange(() => Password);
            NotifyOfPropertyChange(() => CanLogin);
        }
    }

    public bool CanLogin
    {
        get
        {
            bool output;
            output = !string.IsNullOrEmpty(UserName) && !string.IsNullOrEmpty(Password);
            return output;
        }          
    }

    public async Task Login()
    {
        try
        {
            var result = await _apiHelper.Authenticate(UserName, Password);
        }
        catch (Exception ex)
        {
            // TODO - Just there for break points - change to proper error handling - user notification
            Console.WriteLine(ex.Message);
        }
    }
}

The LoginView:

<UserControl x:Class="ARMDesktopUI.Views.LoginView"
    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:ARMDesktopUI.Views"
    mc:Ignorable="d"
    FontSize="24" FontFamily="Montserrat" FontWeight="Light"
    d:DesignHeight="414" d:DesignWidth="790">

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="auto"/>
        <ColumnDefinition Width="auto"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>

    <!-- Header row -->
    <TextBlock Grid.Row="1" Grid.Column="1"
               Grid.ColumnSpan="2"
               HorizontalAlignment="Center"
               Text="Login Form"
               FontSize="76"
               Margin="0 0 0 10" 
               Foreground="#FF99AAC6"/>
    
    <!-- TODO - Stylized separator - Replace with actual styled separator -->
    <Separator Grid.Row="2" Grid.Column="0"
               Grid.ColumnSpan="4"
               VerticalAlignment="Bottom"
               Background="#212121"/>
    <Separator Grid.Row="3" Grid.Column="0"
               Grid.ColumnSpan="4"
               VerticalAlignment="Bottom"
               Margin="0 -0.8 0 1"
               Background="Black"/>
    <Separator Grid.Row="4" Grid.Column="0"
               Grid.ColumnSpan="4"
               VerticalAlignment="Bottom"
               Margin="0 0 0 30"
               Background="#FF454545"/>

    <!-- Username row -->
    <TextBlock Grid.Row="5" Grid.Column="1"
               Text="Username:"
               HorizontalAlignment="Left"
               VerticalAlignment="Center"
               Margin="0 0 0 15"
               Foreground="#FF99AAC6"/>
    <TextBox x:Name="UserName"
             Grid.Row="5" Grid.Column="2"
             FontSize="20"
             Margin="10 0 0 15"
             Style="{StaticResource ModernTextBox}"/>
    
    <!-- Password row -->
    <TextBlock Grid.Row="6" Grid.Column="1"
               Text="Password:"
               HorizontalAlignment="Left"
               VerticalAlignment="Center"
               Margin="0 0 0 30"
               Foreground="#FF99AAC6"/>
    <PasswordBox x:Name="Password" 
                 Grid.Row="6" Grid.Column="2"
                 MinWidth="300" Height="30"                   
                 FontSize="20"
                 Margin="10 0 0 30"
                 Foreground="#FF8897D7"/>

    <!-- Login button row -->
    <Button x:Name="Login"
            Grid.Row="7" Grid.Column="1"
            Grid.ColumnSpan="2"
            HorizontalAlignment="Center"
            Width="439" Height="46"
            Content="Login"
            Foreground="#FF99AAC6"
            Style="{StaticResource LoginButtonTheme}"/>
</Grid>

The TextBoxTheme:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style BasedOn="{StaticResource {x:Type TextBox}}"
       TargetType="{x:Type TextBox}"
       x:Key="ModernTextBox">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TextBox}">
                <Border CornerRadius="10"
                        Background="#353340"
                        MinWidth="300" Height="40">

                    <Grid>
                        <Rectangle StrokeThickness="1"/>
                        <TextBox Margin="1"
                                 Text="{TemplateBinding Text}"
                                 BorderThickness="0"
                                 Background="Transparent"
                                 VerticalContentAlignment="Center"
                                 Padding="5"
                                 Foreground="#CFCFCF"
                                 x:Name="UserName"/>

                        <TextBlock IsHitTestVisible="False"
                                   Text="Username"
                                   VerticalAlignment="Center"
                                   HorizontalAlignment="Left"
                                   Margin="10 0 0 0"
                                   FontSize="20"
                                   Foreground="DarkGray"
                                   Grid.Column="1">

                            <TextBlock.Style>
                                <Style TargetType="{x:Type TextBlock}">
                                    <Style.Triggers>
                                        <DataTrigger Binding="{Binding Text, ElementName=UserName}" Value="">
                                            <Setter Property="Visibility" Value="Visible"/>
                                        </DataTrigger>
                                    </Style.Triggers>
                                    <Setter Property="Visibility" Value="Hidden"/>
                                </Style>
                            </TextBlock.Style>
                        </TextBlock>
                    </Grid>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Can anyone spot where i am confusing Caliburn.Micro with what i am doing?

You should make use of VisualBrush and Triggers to achieve this. For example for UserName, you could use the following,

<TextBox x:Name="UserName" Grid.Row="5" Grid.Column="2"
             FontSize="20"
             Margin="10 0 0 15">
            <TextBox.Style>
                <Style TargetType="TextBox" >
                    <Style.Resources>
                        <VisualBrush x:Key="CueBannerBrush" AlignmentX="Left" AlignmentY="Center" Stretch="None">
                            <VisualBrush.Visual>
                                <Label Content="{Binding CueTextUserName}" Foreground="Gray" />
                            </VisualBrush.Visual>
                        </VisualBrush>
                    </Style.Resources>
                    <Style.Triggers>
                        <Trigger Property="Text" Value="{x:Static system:String.Empty}">
                            <Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
                        </Trigger>
                        <Trigger Property="Text" Value="{x:Null}">
                            <Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
                        </Trigger>
                        <Trigger Property="IsKeyboardFocused" Value="True">
                            <Setter Property="Background" Value="White" />
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </TextBox.Style>
        </TextBox>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM