Lesson 04.02

Style WPF Form from scratch

Let's start again and style the form from scratch. And along the way I will share a few other WPF secrets.

Lesson 04.02

Style WPF Form from scratch

Let's start again and style the form from scratch. And along the way I will share a few other WPF secrets.

Summary (Will be Updated)

Styling Your First WPF Form

Let's copy the XAML code from the previous lesson but without Styles. And now we will go through all controls one by one and style it together.

Here is starting XAML code:

<Window Title="EF-First WPF Form"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Height="Auto" Width="300" MinHeight="150"
        WindowStartupLocation="CenterScreen" ResizeMode="NoResize"
        SizeToContent="Height">
    
    <!--Stack Panel for all elements-->
        <StackPanel Margin="10" >

            <!--First Label and TextBox-->
            <DockPanel Margin="5">
            <TextBlock  Text="Input A" Margin="0,0,5,0" FontWeight="Bold"/>
                <TextBox    Text="Default Value..."  />
            </DockPanel>

            <!--Second Label and TextBox-->
            <DockPanel Margin="5">
            <TextBlock Text="Input B"   Margin="0,0,5,0" FontWeight="Bold"/>
                <TextBox   Text="Default Value..."  />
            </DockPanel>


            <!--Third Label and TextBox-->
            <DockPanel Margin="5">
            <TextBlock Text="Input C" Margin="0,0,5,0" FontWeight="Bold"/>
                <TextBox   Text="Default Value..." />
            </DockPanel>

            <!--Checkboxes-->
            <DockPanel HorizontalAlignment="Center" Margin="5">
                <CheckBox Content="Check 1" Margin="0,0,10,0"/>
                <CheckBox Content="Check 2" Margin="0,0,10,0"/>
                <CheckBox Content="Check 3"                  />

            </DockPanel>

            <!--Separator-->
            <Separator Margin="5,5,5,12" />

            <!--Submit Button-->
            <Button Content="Submit!" 
                FontWeight="Bold"
                Width="100" />

Style - TextBlock

Let's start by creating <Window.Resources> and then we will create the first style for TextBlocks. This is the same as we did in the previous lesson.

  • Create <Style> with TargetType="TextBlock"

  • Override Properties with <Setter>

<Window.Resources>
    
    <!--TEXTBLOCK-->
    <Style TargetType="TextBlock">
        <Setter Property="Foreground" Value="#ff9a2e"/>
        <Setter Property="FontWeight" Value="Bold"/>
        <Setter Property="Margin" Value="0,0,10,0"/>
    </Style>

</Window

TextBox

Next up is the TextBox.

<!--TEXTBOX-->
<Style TargetType="TextBox">
    <Setter Property="Foreground"      Value="999999"  />
    <Setter Property="Background"      Value="2b2b2b"  />
    <Setter Property="BorderBrush"     Value="#ff9a2e" />
    <Setter Property="BorderThickness" Value="0.5"     />
</Style

CheckBox

Next up is the CheckBox.

<!--CheckBox-->
<Style TargetType="CheckBox">
    <Setter Property="Foreground" Value="#ff9a2e"/>
    <Setter Property="Background" Value="#1c1c1c"/>
    <Setter Property="BorderBrush" Value="#ff9a2e"/>
    <Setter Property="BorderThickness" Value="0.5"/>
</Style

Separator

Next up is the Separator.

<!--SEPARATOR-->
<Style TargetType="Separator">
    <Setter Property="Background" Value="#ff9a2e"/>
</Style

Button

Next up is the Button.

<!--BUTTON-->
<Style TargetType="Button">
    <Setter Property="Background" Value="#2b2b2b"/>
    <Setter Property="Foreground" Value="#ff9a2e"/>
    <Setter Property="BorderBrush" Value="#ff9a2e"/>
    <Setter Property="BorderThickness" Value="0.5"/>
</Style

Reuse Color

You might notice that we keep reusing the same colors over and over. And in programming, if you copy-paste something multiple times - it's a sin. And we need to do something about it.

One of the best ways to keep your UI consistent is by defining reusable colors in your Window.Resources and refer to them by key. This approach makes it easier to manage color changes later.


Here is example

<Window.Resources>
    <SolidColorBrush x:Key="accent" Color="#ff9a2e"/>
    <SolidColorBrush x:Key="white" Color="#f2f2f2"/>
    <SolidColorBrush x:Key="gray" Color="#999999"/>
    <SolidColorBrush x:Key="bg1" Color="#1c1c1c"/>
    <SolidColorBrush x:Key="bg2" Color="#2b2b2b"/>

    <!-- OTHER STYLES... -->

Then to access any of these colors you need to use {StaticResource key_name}

Here is an example:

<Window Title="EF-First WPF Form"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Height="Auto" Width="300" MinHeight="150"
        WindowStartupLocation="CenterScreen" ResizeMode="NoResize"
        SizeToContent="Height" Background="#1c1c1c">

    <Window.Resources>


        <!--COLORS-->
        <SolidColorBrush x:Key="accent" Color="#ff9a2e"/>
        <SolidColorBrush x:Key="white" Color="#f2f2f2"/>
        <SolidColorBrush x:Key="gray" Color="#999999"/>
        <SolidColorBrush x:Key="bg1" Color="#1c1c1c"/>
        <SolidColorBrush x:Key="bg2" Color="#2b2b2b"/>



        <!--TEXTBLOCK-->
        <Style TargetType="TextBlock">
            <Setter Property="Foreground" Value="{StaticResource accent}"/>
            <Setter Property="FontWeight" Value="Bold"/>
            <Setter Property="Margin" Value="0,0,10,0"/>
        </Style>


        <!--TEXTBOX-->
        <Style TargetType="TextBox">
            <Setter Property="Foreground" Value="{StaticResource gray}"/>
            <Setter Property="Background" Value="{StaticResource bg2}"/>
            <Setter Property="BorderBrush" Value="{StaticResource accent}"/>
            <Setter Property="BorderThickness" Value="0.5"/>
        </Style>

        <!--CheckBox-->
        <Style TargetType="CheckBox">
            <Setter Property="Foreground" Value="{StaticResource accent}"/>
            <Setter Property="Background" Value="{StaticResource bg1}"/>
            <Setter Property="BorderBrush" Value="{StaticResource accent}"/>
            <Setter Property="BorderThickness" Value="0.5"/>
        </Style>


        <!--SEPARATOR-->
        <Style TargetType="Separator">
            <Setter Property="Background" Value="{StaticResource accent}"/>
        </Style>

        <!--BUTTON-->
        <Style TargetType="Button">
            <Setter Property="Background" Value="{StaticResource bg2}"/>
            <Setter Property="Foreground" Value="{StaticResource accent}"/>
            <Setter Property="BorderBrush" Value="{StaticResource accent}"/>
            <Setter Property="BorderThickness" Value="0.5"/>

        </Style>

    </Window.Resources>


    <!--Stack Panel for all elements-->
    <StackPanel Margin="10" >

        <!--First Label and TextBox-->
        <DockPanel Margin="5">
            <TextBlock  Text="Input A"   />
            <TextBox    Text="Default Value..."  />
        </DockPanel>

        <!--Second Label and TextBox-->
        <DockPanel Margin="5">
            <TextBlock Text="Input B"  />
            <TextBox   Text="Default Value..."  />
        </DockPanel>


        <!--Third Label and TextBox-->
        <DockPanel Margin="5">
            <TextBlock Text="Input C"  />
            <TextBox   Text="Default Value..." />
        </DockPanel>



        <!--Checkboxes-->
        <DockPanel HorizontalAlignment="Center" Margin="5">
            <CheckBox Content="Check 1" Margin="0,0,10,0" IsChecked="True"/>
            <CheckBox Content="Check 2" Margin="0,0,10,0"/>
            <CheckBox Content="Check 3"                  />

        </DockPanel>

        <!--Separator-->
        <Separator Margin="5,5,5,12" />

        <!--Submit Button-->
        <Button Content="Submit!" 
                FontWeight="Bold"
                Width="100" />

    </StackPanel>

</Window>
Window Gradient Background

If you want to make you form pop a little more, you can also introduce Gradient Colors. Let's add a gradient to our <Window.Background>.

Firstly, since we can't define it in 1 line, we need to access Window Background not through the property in the StartTag, but we need to open it up and write the Content of Background Property.

Here is my very subtle gradient:

<Window.Background>
    <LinearGradientBrush StartPoint="0,1" EndPoint="1,0">
        <GradientStop Color= "#1c1c1c" Offset="0" />
        <GradientStop Color= "#2b2b2b" Offset="0.5" />
        <GradientStop Color= "#1c1c1c" Offset="1" />

Here is how my form looks so far

If you want to make it look a more interesting, you can try other Gradients.

Here is an example of a gradient I used in my EF-Tools extension.
Feel free to steal that one.

<Window.Background>
    <LinearGradientBrush StartPoint="0,1" EndPoint="1,0">
        <GradientStop Color= "#55FC467B" Offset="0" />
        <GradientStop Color= "#553F5EFB" Offset="1" />

This one looks nicer, but make sure to adjust other colors to match it.

Orange is not the best choice for that one.

Test form in Revit

Also if you want to test this form inside of Revit, you can use the same WPF Base Class that we made in all other lessons. We don't have any Events, so it's super simple.

  • Copy XAML code to pushbutton

  • Create WPF Class

#⬇️ IMPORTS
from Autodesk.Revit.DB import *
from pyrevit import forms   # By importing forms you also get references to WPF package! IT'S Very IMPORTANT !!!
import wpf, os, clr         # wpf can be imported only after pyrevit.forms!

# .NET Imports
clr.AddReference("System")
from System.Collections.Generic import List
from System.Windows import Window, ResourceDictionary
from System.Windows.Controls import CheckBox, Button, TextBox, ListBoxItem


#📦 Variables
PATH_SCRIPT = os.path.dirname(__file__)
doc     = __revit__.ActiveUIDocument.Document #type: Document
uidoc   = __revit__.ActiveUIDocument
app     = __revit__.Application


#📊 Main Form
# Inherit .NET Window for your UI Form Class
class MyForm(Window):
    def __init__(self):
        # Connect to .xaml File (in the same folder!)
        path_xaml_file = os.path.join(PATH_SCRIPT, 'StyleForm.xaml')
        wpf.LoadComponent(self, path_xaml_file)

        # Show Form
        self.ShowDialog()


#🎯 Show form to the user
UI = MyForm()

Form Flaws

Alright, our form looks fine, until we actually start to interact with it. Then we notice some flaws.

  • Tick inside CheckBox is not visible

  • CheckBox Hover effect

  • Button Hover Effect

We will address the Checked state of the checkbox now, but for the hover effect we will need to dive into another concept in the next lesson. Let's fix the checked state first.

Style Triggers

We've set the Background and Foreground of our <CheckBox>, but now we want to change it when it's Checked. To do that ,we need to use <Style.Triggers> in XAML code. And it's not complicated, it's like creating an if-statement.

Here is the code:


Alright, our form looks fine, until we actually start to interact with it. Then we notice some flaws.

You will notice that when we ho

Now we’ll add some interactivity to the CheckBox by changing its background color when it’s checked. This is where triggers come in handy.

<Style TargetType="CheckBox">
    <Setter Property="Foreground" Value="{StaticResource accent}"/>
    <Setter Property="Background" Value="{StaticResource bg1}"/>
    <Setter Property="BorderBrush" Value="{StaticResource accent}"/>
    <Setter Property="BorderThickness" Value="0.5"/>

    <Style.Triggers>
        <Trigger Property="IsChecked" Value="True">
            <Setter Property="Background" Value="{StaticResource accent}"/>
        </Trigger>
    </Style.Triggers>
</Style

The Trigger here changes the CheckBox background to the accent color when it is selected, creating a clear visual indication of the checked state.

🙋‍♂️ See you in the next lesson.
- EF

🙋‍♂️ See you in the next lesson.
- EF

Discuss the lesson:

P.S. Sometimes this chat might experience connection issues.
Please be patient or join via Discord app so you can get the most out of this community and get access to even more chats.

Discuss the lesson:

P.S. Sometimes this chat might experience connection issues.
Please be patient or join via Discord app so you can get the most out of this community and get access to even more chats.