0

Context

I'm currently doing a project with WPF, which I'm fairly new to. What I am trying to do is a custom and configurable taskbar-like application.

What I currently have

I managed to create a dynamic interface only containing buttons that looks good to me, here is what it looks like :

TaskbarShowCase

And here is the xaml code that I wrote :

<Window x:Class="RaccourciProgrammesUI.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:RaccourciProgrammesUI"
        mc:Ignorable="d"
        Title="Fenêtre principale" 
        WindowStyle="None"
        ResizeMode="CanMinimize"
        Width="40"
        LostFocus="Window_LostFocus"
        LostKeyboardFocus="Window_LostKeyboardFocus">

    <!-- Container -->
    <Grid x:Name="ButtonsContainer" Height="{Binding ButtonsContainerHeight}">

        <!-- Row dimensions -->
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <!-- Closing button -->
        <Button Grid.Row="0"
                Background="{Binding CloseAppButton.BackgroundColor}"
                BorderBrush="#686868"
                Foreground="White"
            
                Name="CloseAppButton"
                Height="40"
                Click="CloseAppButton_Click">
            <Grid>
                <Image Source="{Binding CloseAppButton.PictureBitmap}"
                       RenderOptions.BitmapScalingMode="Fant"
                       Stretch="Uniform"
                       Height="{Binding CloseAppButton.PictureHeight}"/>
            </Grid>
        </Button>

        <!-- Dynamic buttons -->
        <ItemsControl Grid.Row="1"
                      ItemsSource="{Binding ContainersData}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Button 
                        Background="#2d2d30"
                        BorderBrush="#686868"
                        Foreground="White"
                        
                        Height="40"
                        Click="DynamicButton_Click">
                        <Grid>
                            <Image Source="{Binding Path=PictureBitmap}"
                                   RenderOptions.BitmapScalingMode="Fant"
                                   Stretch="Uniform"
                                   Height="{Binding Path=PictureHeight}"/>
                        </Grid>
                    </Button>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

        <!-- Add element button -->
        <Button Grid.Row="2"
                Background="{Binding AddButton.BackgroundColor}"
                BorderBrush="#686868"
                Foreground="White"
            
                Name="AddButton"
                Height="40"
                Click="AddButton_Click">
            <Grid>
                <Image Source="{Binding AddButton.PictureBitmap}"
                       RenderOptions.BitmapScalingMode="Fant"
                       Stretch="Uniform"
                       Height="{Binding AddButton.PictureHeight}"/>
            </Grid>
        </Button>

    </Grid>
</Window>

The button with Chrome and Visual Studio 2019 icons are dynamically created from data stored in a .json file.

What I want to do

Now what I would like to do is when I click on the Chrome or Visual Studio button, it would display another interface looking exactly the same but with different content, right next to the left side of the "taskbar", kinda like a MenuItem would do. Let's say I clicked on the Chrome button, I would like to do something like that :

RepresentationInterfaceWanted

(I wrote numbers only for showcasing purpose, it would contain other data in real conditions)

The question

What I would like to know is : is it better to create a MenuItem or something equivalent and to customize it a lot to make it look like what I want, or should i just open a new Window using the same style, even if it might not be an optimal solution (2 windows for 1 menu, potential difficulties to size/place it correctly)? I might not be aware of many possibilities of doing it otherwise, so feel free to suggest any idea!

Update : Here is what I did for now, is it an acceptable solution?

Context

A few days have passed and I managed to do this interface the way I wanted, but I am curious if it is r/programminghorror material here.

What it looks like

Here is what i managed to do :

Update1ResultPreview

(Please ignore the "+", "-", and "settings" buttons, they are not impacting the subject of this question)

As you can see, I have like a MenuItem that opens next to the clicked button (it is absolutely not a MenuItem that I modified, it just looks like it a little bit). This menu's height change dynamically with the number of buttons in it, and the center of this menu stays next to the chrome button.

How I did it

For understanding purpose, let's call buttons with Chrome or VS 2019 pictures "Containers". I decided that because they act like element that "contains" buttons using this app. By doing this, we can differentiate "Containers" and real "Buttons" that are in the opened menu.

The trick I used is to set 2 columns in the main grid (the one called ButtonsContainer in my unedited question), and I use the Visibility property to manage if it is shown or not. This way, I didn't have to customize a MenuItem a lot, neither did I have to open another window!

To position correctly the button column, I apply a Thickness to the margin property, allowing me to move it up or down, right next to the container I clicked (this is made by calculation, no hard value used or anything like that).

Here is the current xaml code :

<Window x:Class="TaskBarBis.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TaskBarBis"
        xmlns:viewmodel="clr-namespace:TaskBarBis.ViewModel" 
        d:DataContext="{d:DesignInstance Type=viewmodel:MainWindowVM}"
        mc:Ignorable="d"
        Title="TaskBarBis" 
        WindowStyle="None"
        ResizeMode="CanMinimize"
        LostFocus="Window_LostFocus"
        LostKeyboardFocus="Window_LostKeyboardFocus"
        AllowsTransparency="True"
        Background="Transparent"
        Opacity="0.9">

    <Grid>

        <!-- Column dimensions -->
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>

        <!-- Containers column -->
        <Grid Grid.Column="1"
              Name="ContainersButtonField" 
              Height="{Binding ButtonsContainerHeight}"
              Width="40">

            <!-- Row dimensions -->
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="*" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>

            <!-- Closing button -->
            <Button Grid.Row="0"
                    Background="#df0024"
                    BorderThickness="0"
                    BorderBrush="#686868"
                    Foreground="White"
                    Name="CloseAppButton"
                    Height="40"
                    Click="CloseAppButton_Click">
                <Grid>
                    <Image Source="{Binding CloseAppButton.PictureBitmap}"
                           RenderOptions.BitmapScalingMode="Fant"
                           Stretch="Uniform"
                           Width="{Binding CloseAppButton.PictureWidth}"/>
                </Grid>
            </Button>

            <!-- Dynamic containers -->
            <ItemsControl Grid.Row="1"
                          ItemsSource="{Binding ContainersData}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Button Background="{Binding BackgroundColor}"
                                BorderThickness="0"
                                BorderBrush="#686868"
                                Foreground="White"
                                Height="40"
                                Click="DynamicContainer_Click">
                            <Grid>
                                <Image Source="{Binding PictureBitmap}"
                                       RenderOptions.BitmapScalingMode="Fant"
                                       Stretch="Uniform"
                                       Height="{Binding PictureHeight}"/>
                            </Grid>
                        </Button>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>

            <!-- Add element button -->
            <Button Grid.Row="2"
                    Background="{Binding AddButton.BackgroundColor}"
                    BorderThickness="0"
                    BorderBrush="#686868"
                    Foreground="White"
                    Name="AddButton"
                    Height="40"
                    Click="AddButton_Click">
                <Grid>
                    <Image Source="{Binding AddButton.PictureBitmap}"
                           RenderOptions.BitmapScalingMode="Fant"
                           Stretch="Uniform"
                           Width="{Binding AddButton.PictureWidth}"/>
                </Grid>
            </Button>

            <!-- Delete element button -->
            <Button Grid.Row="3"
                    Background="{Binding DeleteButton.BackgroundColor}"
                    BorderThickness="0"
                    BorderBrush="#686868"
                    Foreground="White"
                    Name="DeleteButton"
                    Height="40"
                    Click="DeleteButton_Click">
                <Grid>
                    <Image Source="{Binding DeleteButton.PictureBitmap}"
                           RenderOptions.BitmapScalingMode="Fant"
                           Stretch="Uniform"
                           Width="{Binding DeleteButton.PictureWidth}"/>
                </Grid>
            </Button>

            <!-- Setting button -->
            <Button Grid.Row="4"
                    Background="{Binding SettingsButtonBackgroundColor}"
                    BorderThickness="0"
                    BorderBrush="#686868"
                    Foreground="White"
                    Name="SettingsButton"
                    Height="40"
                    Click="SettingsButton_Click">
                <Grid>
                    <Image Source="{Binding SettingsButton.PictureBitmap}"
                           RenderOptions.BitmapScalingMode="Fant"
                           Stretch="Uniform"
                           Width="{Binding SettingsButton.PictureWidth}"/>
                </Grid>
            </Button>
        </Grid>

        <!-- Dynamic buttons column -->
        <Grid x:Name="DataButtonsField"
              Grid.Column="0"
              Visibility="{Binding DisplayButtonsData}"
              Height="{Binding DataButtonsHeight}"
              MaxWidth="600"
              HorizontalAlignment="Right">

            <ItemsControl Grid.Row="0"
                          ItemsSource="{Binding ButtonsData}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Button Background="#2d2d30"
                                BorderBrush="#686868"
                                Foreground="White"
                                Height="40"
                                MinWidth="40"
                                Padding="10,0,10,0"
                                Click="DynamicButton_Click">
                            <Grid>

                                <!-- Row dimensions -->
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto" />
                                    <ColumnDefinition Width="Auto" />
                                </Grid.ColumnDefinitions>

                                <Image Grid.Column="0"
                                       Source="{Binding PictureBitmap}"
                                       RenderOptions.BitmapScalingMode="Fant"
                                       Stretch="Uniform"
                                       Width="{Binding PictureWidth}"/>

                                <Border Grid.Column="1">
                                    <TextBlock Text="{Binding Content}"
                                               VerticalAlignment="Center"/>
                                </Border>
                            </Grid>
                        </Button>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </Grid>

    </Grid>
</Window>

To manage the display of the menu, I simply modify one value of the ViewModel in the code behind the view when I click on a container. The binding and INotifyPropertyChanged interface does it's tricks to actualize the value on the View.

This way, when I click on a Container, the only things I have to change is 2 property in the ViewModel (the display property and the buttons used in the column property to feat the container's content), and the margin of the container column.

The question

Is it an okay solution? I think it's pretty convenient for the UI, it doesn't need an extra window and it's pretty simple to use once it is setup (2 property of the ViewModel to change and recalculate a margin). Will I find my post in r/programminghorror?

Of course, feel free to suggest anything that you think would be good!

4
  • The one vote to close (not from me) is because this is better asked on StackOverflow which is for specific implementation questions. I do agree with that. The solution looks OK. Jul 7 at 13:14
  • Well, I asked this on Software Engineering when I didn't have anything done and asking what would be the best way to do it, Stack Overflow doesn't allow this. But I do agree too that the update part may not 100% fit Software Engineering spirit. Thanks for sharing your thoughts! Jul 7 at 13:28
  • 1
    Your solution is perfectly fine. the only possible recommendation is to change the click-handler to use Command objects. You can bind the commands to any click handler and that way you can have the model behind each tile handle it's own clicks. Up to you though. Jul 8 at 10:55
  • That's a nice idea, I didn't think about that! I'll implement it next time I'll work on the project, thanks a lot for taking time to look at my post and suggesting :) Jul 8 at 14:01

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.