VS2017 – Oh so you zoomed in by accident…

One of the really nice things about VS2017 is the ctrl+. functionality. It takes away a lot of the need for ReSharper, so much so that I’m currently trying to see if I can live without R# on one of my installs.

However, if you are as fat fingered as I am then you’ve probably also found the ctrl-shift+. key combination and now you have a quandry.

Firstly, your screen just zoomed in and while you quite like it, you want it to go back. Ctrl-Shift-, isn’t bound to anything that would be too obvious. So you reach for the mouse and hit the drop down and all is well again, until you fat finger again.

Maybe if you search in ctrl-q you can find out what happened? Nope.

“Zoom” seems to give no results except for search NuGet (and I’m on a very flaky mobile connection here). Nor “enlarge”, “view” argh!!!

About this point I searched the Tools->Options->Keyboard settings until I uncovered that Ctrl-Shift+. is bound to View.Zoomin, and Ctrl-Shift+, isn’t bound to anything.

So here’s the choice.

Bind Ctrl-Shift+, to View.Zoomout, or unbind Ctrl-Shift+.

Personally, I quite like this keyboard zooming lark, and just need to found out if VS syncs this setting properly…

Advertisements

UWP Combobox SelectedItem doesn’t bind

I’ve had problems using UWP ComboBoxes, the symptoms being that even by Binding the SelectedItem, nothing would be selected in the ComboBox when it was displayed. Sometimes this showed as, you could select something go, leave the page and return and the selection would be cleared, or even worse, it works on some visits to a page but not others when you hadn’t changed the selection.

So here’s some causes.

The collection items changes

Illistrated as an example on Stackoverflow. Basically if you re-create your collection items then it is impossible for selected item to be found in it, i.e. it looks like SelectedItem uses ReferenceEquals to find the item in the collection.

So this code has problems because it recreates the collection AND its items on every get,

public Item[] Items => new[] 
{ 
    new Item {Index = 1}, 
    new Item {Index = 2}, 
    new Item {Index = 3}, 
    new Item {Index = 4} 
};

but this is fine as the strings are constant, even though the collection changes

public string[] Strings => new[] {"1", "2", "3", "4"};

You specify a field instead of a property

I have no idea why this doesn’t work, but… if you specify a field

public string[] Strings = {"1", "2", "3", "4"};

then the binding will fail to select the items again, it needs to be specified as

public string[] Strings {get;} = {"1", "2", "3", "4"};

You specify the XAML in the wrong order

This is the massive WTF, and yet it is quite obvious really. I had the following code

<ComboBox SelectedItem="{x:Bind FirstItem}" ItemsSource="{x:Bind Items}"> ….

When you NEED to have

<ComboBox ItemsSource="{x:Bind Items}" SelectedItem="{x:Bind FirstItemMode=TwoWay, Mode=TwoWay}">

In this case, during the bind of the SelectedItem it is checked against an empty collection, and as a result, the selected Item ends up being set to null. Then the Items are bound.

Debugging

One thing that helps is that if you switch your binding to Mode=OneWay then the binding will display correctly in the ComboBox (but obviously won’t update). Which does at least give us a starting point.

WPF: Re-creating VS2012 window glow

WPF: Re-creating VS2012 window glow

WPF is such a powerful technology that I’m reminded of the Perl mantra, there’s more than one way to do it. So rather than just giving you some code, this post is also about explaining why I’ve used several WPF techniques so that you can find some reuse for them too.

Window Glow

It seems such an easy thing to do at first, just to add a bit of outer glow chrome around the window and highlight your window amongst the stark simplicity of modern windows. The most naive implementation of this would be to simply building up some glow around a single pixel of colour. In fact this (very bad) piece of code  below does exactly that, just by layering several borders;

<Window x:Class="VS2012OuterGlowArticle.MainWindow" 
xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation 
xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml 
Title="MainWindow" 
Height="350" Width="525"> 
 <Border Padding="1" CornerRadius="8">
 <Border.Background> 
 <SolidColorBrush Color="DarkOrange" Opacity="0.06"/> 
 </Border.Background> 
 <Border Padding="1" CornerRadius="8"> 
 <Border.Background>
  <SolidColorBrush Color="DarkOrange" Opacity="0.06"/> 
 </Border.Background> 
 <Border Padding="1" CornerRadius="8"> 
 <Border.Background>
 <SolidColorBrush Color="DarkOrange" Opacity="0.06"/>
 </Border.Background> 
 <Border Padding="1" CornerRadius="8">
 <Border.Background> 
 <SolidColorBrush Color="DarkOrange" Opacity="0.06"/>
 </Border.Background>
 <Border Padding="1">
 <Border.Background>
 <SolidColorBrush Color="DarkOrange" Opacity="0.06"/>
 </Border.Background>
 <Border BorderBrush="DarkOrange" BorderThickness="1" Background="White"/>
 </Border>
 </Border>
 </Border>
 </Border>
 </Border>
 </Window> 

And looks like thisglow2

Obviously there are a few flaws with this, but it does highlight exactly why I think WPF is a great advance over WinFoms. The primitives that we can build from in WPF are very flexible and can be used to create fabulous effects when we don’t know another way to do it.

Effects

But WPF also provides some great built in effects and one of these makes our life much simpler. We can use the DropShadowEffect without any ShadowDepth to give us an outer glow with far less code.

<Window x:Class="VS2012OuterGlowArticle.MainWindow" 
xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation 
xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml 
Title="MainWindow" 
Height="100" Width="200" >
 <Border BorderBrush="DarkOrange" BorderThickness="1" Background="White" Margin="5">
 <Border.Effect>
 <DropShadowEffect ShadowDepth="0" BlurRadius="5" Color="DarkOrange"/>
 </Border.Effect>
 </Border>
</Window> 

 

glow3

Outside not inside

Now you might be wondering why I haven’t tried to put the outer glow on the outside of the window so far, well there is a very simple reason, windows even under the post Vista world, still have hard region limits. If we add the effect to the window it attempts to draw outside of the region, and simply gets clipped.

<Window x:Class="VS2012OuterGlowArticle.MainWindow" 
 xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation 
 xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml 
 Title="MainWindow" 
 Height="350" Width="525"> 
 <Window.Effect> <!-- This doesn't work, shame--> 
 <DropShadowEffect ShadowDepth="0" BlurRadius="5" Color="DarkOrange"/>
 </Window.Effect>
 <Grid>
 </Grid>
</Window> 

Hmmm so maybe that’s why…

So at this point, we can see that we are going to need to provide the transparency inside the region of our window. Maybe that’s why the VS2012 window is drawn with a completely custom chrome?

If we just try setting our Window.Background=”Transparent” nothing happens again, but that is primarily because we haven’t turned on Window.AllowsTransparency=”true” as well, and unfortunately running the code at this point results in an error telling us the only acceptable WindowStyle is WindowStyle=”None”.

 

 

<Window x:Class="VS2012OuterGlowArticle.MainWindow" 
 xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation 
 xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml 
 Title="MainWindow" 
 Height="100" Width="200" 
 AllowsTransparency="True" WindowStyle="None" Background="Transparent"> 
 <Border BorderBrush="DarkOrange" BorderThickness="1" Background="White" Margin="5">
 <Border.Effect>
 <DropShadowEffect ShadowDepth="0" BlurRadius="5" Color="DarkOrange"/>
 </Border.Effect>
 </Border>
</Window> 

 

glow4

As you can see we now have our glow but lack everything else. However I think this once again helps us as it frees us from the constraints of the OS look and lets us do whatever else we want to. You can get most of your functionality back by integrating the Microsoft.Windows.Shell library. This is a superset of System.Windows.Shell and includes handling extending glass which we can subvert to provide such things as window drag and resize borders. Normally I have all of this in a style, and so the relevant part looks like this.

<Setter Property="shell:WindowChrome.WindowChrome"> 
 <Setter.Value>
 <shell:WindowChrome 
 ResizeBorderThickness="4" CaptionHeight="24" 
 CornerRadius="0" GlassFrameThickness="0"/>
 </Setter.Value>
</Setter>

For now, however I’m going to simply add a close button

<Grid>
 <Button HorizontalAlignment="Right" VerticalAlignment="Top"
 Background="DarkOrange" Foreground ="White" BorderThickness="0"
 Click="Window_Close">
 X
 </Button>
</Grid> 

glow5

 

Maximised windows

Even though we haven’t provide the UI to allow the user to maximise the window with the mouse, you can still use the Windows7 shortcuts, Windows+Up/Left/Right, and there is a problem. Remember that margin that we needed around our border to allow us the room for our gradient, well its still there.

If you maximise your window you also are still left with the single pixel Border all round your window, but we can quite easily deal with this. I first used a custom converter on a Binding from WindowState that produces a zero Thickness for the border when WindowState=”Maximised”.

But if you snap your window to the left or right with Windows+Left/Right you can also see it quite plainly, and this time the window hasn’t changed state, just size.

glow6

Well, I suppose I could just check on when the window size changes and just turn off the relevant borders, but unfortunately there doesn’t seem to be an easy way to find the size of the working area (ie. the bit without the the taskbar) of the screen in WPF. You can get it for the Primary screen, but not the current. Which is fine as long as you are sure nobody is ever going to run your application on multiple monitors.

In fact, some investigation of this lead me to http://stackoverflow.com/questions/1927540/how-to-get-the-size-of-the-current-screen-in-wpf and an answer by Nils http://stackoverflow.com/users/180156/nils where he wraps the WinForms assembly in a nice class called WpfScreen, and this allows us to set both the border and the BorderThickness containing the glow.

This allows me to simply determine the border and margin (where the glow appears) to be calculated like this;

if (window.WindowState == WindowState.Maximized)
{
 //No point in glowing when you cant see the glow 
 border.Margin = new Thickness(0); 
 border.BorderThickness = new Thickness(0); 
 return; 
} 
// Snapped windows are a little more awkward to detect 
// todo: Need to get current screen height not primary 
var workingArea = WpfScreen.GetScreenFrom(window).WorkingArea; 
if (window.Top == workingArea.Top && window.Height == workingArea.Height) 
{ 
 border.Margin = new Thickness( 
 window.Left == workingArea.Left ? 0 : GLOW_BORDER_MARGIN, 
 0, 
 window.Left + window.Width == workingArea.Right ? 0 : GLOW_BORDER_MARGIN,
 0); 
 border.BorderThickness = new Thickness( 
 window.Left == workingArea.Left ? 0 : 1, 
 0, 
 window.Left + window.Width == workingArea.Right ? 0 : 1, 
 0); 
 return; 
} 
border.Margin = new Thickness(GLOW_BORDER_MARGIN); 
border.BorderThickness = new Thickness(1);

And as they say, here’s one I made earlier; notice how the top glow is missing since its snapped to the left hand side.

glow7

Reusability

Now at this point you have enough to go off and replicate this effect. Just add a Window_SizeChanged handler with the above snippet, add a border with it’s effect into the window xaml, and include Microsoft.Windows.Shell assembly for window resizing and repositioning and your done. However applications often need more than one window and there’s always the next application too, so we want to avoid copying and pasting the code around.

Normally we would just use a derived class to handle this, but the problem here is that the user may already be using a UI framework that adds some functionality, possibly even a corporate standard one that they can’t change and is now just looking just to change the look and feel. We could use a Mixin class that allows us to include the additional functionality, but then the user would still be need to add initialisation code to the code-behind in order to trigger it, which isn’t really look-less really.

Fortunately WPF provides the means with attached properties. We can define an attached property so that when we set some value in the xaml, then this triggers a registration process. Since we need to identify which border we are going to be switching on and off, then I’ve chosen to make this the value I need to set in my property, i.e.

<Border BorderThickness="1" BorderBrush="DarkOrange" WinChrome:Modern.GlowBorder="True">
 ...
</Border>

And finally

Since the ultimate form of re-usability these days seems to come from open-sourcing ones code, I’m dropping all of this into winchrome.codeplex.com.