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 :
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 :
(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 :
(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!
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.