Introduction
It has been a long time since my last tip, a tip that luckily was turned obsolete by the nice addition of the GridSplitter in the always helpful Windows Community Toolkit.
This time, I decided to redeem myself by providing an insanely simple and smart way to implement draggable behavior from scratch,.
More specifically, I will implement a way to resize a SplitView
Pane, something that I think is probably the most practical use of a Draggable
function and GridSplitter
cannot do.
This will be a comparably long article if you consider the simplicity of the implementation, this is because I put on an introductory/educational character to it, so if you are the kind of guy that feeds on sample code, just go ahead and check the GitHub repo.
Background
You will need:
- Visual Studio 2017 with UWP dev kit installed on a recent SDK version
- Knowing how to write a few lines of code
Implementation
Setting Up
First things first, we create a new UWP Project and set the solution name as “DragSplitView
”.
Wait for it to load and then we go and open the MainPage.xaml.
Now we will begin to carve out the skeleton of our Design, we will name the main Grid
of the page as MasterGrid
and add the following RowDefinitions
to it:
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
On the First Row, we assign a ToggleButton
and we call it “OpenButton
”, we set IsChecked="True"
IsChecked="True"
, we assign an icon inside it: <SymbolIcon Symbol="More" />
On the Second Row, we will assign a new SplitView
, we call that “CoreSplitView
”, we set DisplayMode="Inline"
.
For more information about Grid and Placement Definitions, check the Appendix below.
The End Result should look like this:
<Page
x:Class="DragSplitView.MainPage"
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:local="using:DragSplitView"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
<Grid Name="MasterGrid">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<ToggleButton Name="OpenButton" IsChecked="True">
<SymbolIcon Symbol="More" />
</ToggleButton>
<SplitView Name="CoreSplitView"
Grid.Row="1"
DisplayMode="Inline">
</SplitView>
</Grid>
</Page>
Basic Binding
It is time to implement the basic Interactivity of our App, to do this, we will use the Binding system, if you are new to Binding and want to learn more, check this out.
We will control the Open State of our SplitView
Pane, in other words, the checked state of our ToggleButton
will reflect the state of our SplitView
, this is easily done through Binding.
We will add the following inside our SplitView
Properties:
IsPaneOpen="{Binding IsChecked,ElementName=OpenButton}"
OpenPaneLength="400"
You can now run the program and see how the button controls the state, notice how element ElementName
, refers to our ToggleButton
, and that IsChecked
refers to the involved property inside that ToggleButton
.
Finishing Up
We will finalize the UI Layout so we can move to the last Phase.
We go ahead and expose the <SplitView.Pane>
and we add a Grid
called "PaneGrid
" and set on Background="Gold"
, this Grid
on its own accord will host the following new control:
<Slider Name="MasterSlider"
MinWidth="480"
VerticalAlignment="Stretch"
Maximum="480"
Minimum="20"
Opacity="0"
Value="150" />
This is a Slider control and it serves a core purpose, we will dive deeper later.
Below this Slider, we add this:
<StackPanel
Name="PaneStackPanel"
Margin="0,0,20,0"
Background="MediumAquamarine">
<TextBlock TextWrapping="Wrap" Text="this is a custom draggable implementation that is pretty cool,
for more stuff check me out at www.codeproject.com/Members/Georgemns" />
</StackPanel>
Under </SplitView.Pane>
, we will add a Grid
called "ContentGrid
" and set on Background="LightSteelBlue"
.
Last, but not the least important action is to change the:
OpenPaneLength="400"
to:
OpenPaneLength="{Binding Value, ElementName=MasterSlider, Mode=OneWay}"
As you might have guessed, Value
is the current Progress of our Slider, and thanks to this statement, it will be "connected" to OpenPaneLength, in a OneWay mode.
So that’s all you gotta do, if you have been following so far, here is how the document should look:
<Page x:Class="DragSplitView.MainPage"
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:local="using:DragSplitView"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
<Grid Name="MasterGrid">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<ToggleButton Name="OpenButton" IsChecked="True">
<SymbolIcon Symbol="More" />
</ToggleButton>
<SplitView Name="CoreSplitView"
Grid.Row="1"
DisplayMode="Inline"
IsPaneOpen="{Binding IsChecked,ElementName=OpenButton}"
OpenPaneLength="{Binding Value, ElementName=MasterSlider, Mode=OneWay}" >
<SplitView.Pane>
<Grid Name="PaneGrid" Background="Gold">
<Slider Name="MasterSlider"
MinWidth="480"
VerticalAlignment="Stretch"
Maximum="480"
Minimum="20"
Opacity="0"
Value="150" />
<StackPanel Name="PaneStackPanel"
Margin="0,0,20,0" Background="MediumAquamarine">
<TextBlock TextWrapping="Wrap" Text="this is a custom draggable implementation
that is pretty cool, for more stuff check me out
at www.codeproject.com/Members/Georgemns" />
</StackPanel>
</Grid>
</SplitView.Pane>
<Grid Name="ContentGrid" Background="LightSteelBlue">
</Grid>
</SplitView>
</Grid>
</Page>
Dissection
As I said, Slider is the heart of this system, Slider’s Current Progress (Value
) directly reflects to the current Pane Width of the Pane, through the Binding expression showcased above and much like how implemented the open-close function with that button.
Now into more specifics:
- We assign
MinWidth="480"
, Maximum="480"
and Minimum="20"
on that Slider because the slider would otherwise change width and “compress” thus losing the 1-1 pixel to progress parity we have, 480 is just an arbitrary number that we choose to be as the Maximum potential Width that our pane can Expand to, likewise Minimum is the Minimum Width of the Pane, it is good to note that you should seek for a Maximum number that is below 500, as this is the default minimum window size. - We also set
Value="150"
, as the starting point when our app starts up, you might choose to expand this implementation by saving the current value and then load it back up when your app starts again. VerticalAlignment =”Stretch”
, this what allows the Slider to cover up the entire Vertical Space, in other words, the slider will no longer act like a thin strip of color, but an entire area that can potentially respond to any user input inside the Pane. Opacity="0"
, this is a smart way of making something invisible, notice how we don’t use the actual Visibility
property, this is because turning down the opacity will still allow us to interact with the bar even if you don’t actually see it.
The StackPanel
that is directly under our slider, acts like a cover for our Slider. Notice how it has Margin="0,0,20,0"
, this translates to a 20px distance from the right wall, which allows the Slider Area to be exposed as a golden strip that is used to drag the Pane, again the 20px is arbitrary but it has to match the Minimum of the Slider.
History
- 27th September, 2018: Published