I've just had the pleasure of getting into the UWP-stuff and I have to say: I hate it ... after less than 12 hours of testing with it. I'll just show off my journey about this and ignore the UWP until one is able to use it.
First things first. I just tried to copy&paste a known pattern from WPF to UWP. Simple RelayCommand/DelegateCommand/Command/Whatever you'll call it. That's simple code, right? Let's get into it. Here's my WPF version of a RelayCommand (nothing special):
public class RelayCommand : ICommand, IDisposable
{
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
delegates.Add(value);
}
remove
{
CommandManager.RequerySuggested -= value;
delegates.Remove(value);
}
}
private Func<bool> canExecuteFunc;
private List<EventHandler> delegates = new List<EventHandler>();
private Action executeAction;
public RelayCommand(Action executeAction) : this(executeAction, () => true)
{
}
public RelayCommand(Action executeAction, Func<bool> canExecuteFunc)
{
this.executeAction = executeAction;
this.canExecuteFunc = canExecuteFunc;
}
public bool CanExecute(object parameter)
{
return canExecuteFunc();
}
public void Dispose()
{
RemoveAllEvents();
}
public void Execute(object parameter)
{
executeAction();
}
public void RemoveAllEvents()
{
foreach (EventHandler eh in delegates)
{
CommandManager.RequerySuggested -= eh;
}
delegates.Clear();
}
}
Sure it could be better and performance optimized but let's keep it like that. Simple and working. I tried copying that into a UWP project and failed because CommandManager
does not exist anymore. Because WHO COULD PROBABLY USE THIS!? (apart from all WPF Devs)
Okay .. no problem. Removing this and keeping everything clean .. with a simple event and stuff. Solution would be the following:
public class RelayCommand : ICommand, IDisposable
{
public event EventHandler CanExecuteChanged;
private Func<bool> canExecuteFunc;
private Action executeAction;
public RelayCommand(Action executeAction) : this(executeAction, () => true)
{
}
public RelayCommand(Action executeAction, Func<bool> canExecuteFunc)
{
this.executeAction = executeAction;
this.canExecuteFunc = canExecuteFunc;
}
public bool CanExecute(object parameter)
{
return canExecuteFunc();
}
public void Dispose()
{
RemoveAllEvents();
}
public void Execute(object parameter)
{
executeAction();
}
public void RemoveAllEvents()
{
CanExecuteChanged = null;
}
}
But it's not working and Command-Bindings are not working ... because CanExecuteChanged would never be raised. Reading a bit about this Command-Stuff I added a function to raise this event.
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
Thought it works now? Not even close. I have to admit: It's working. I just made a mistake in writing the RelayCommand. With
privat RelayCommand command;
public RelayCommand Command => command ?? (command = new RelayCommand(() => { }));
It's working again. Screw me.
Declare a command like this
public RelayCommand BackCommand => new RelayCommand(() => { ; }, () => DateTime.Now.Ticks % 2 == 0);
and have a <RadioButton Command="{Binding BackCommand}" />
in your view and well ... it's not updating ... what a surprise. Now for test purpose add a button to the view which calls a command (without second parameter) which just raises the CanExecuteChanged-Event.public RelayCommand TestCommand => new RelayCommand(() => BackCommand.RaiseCanExecuteChanged());
That's it, right? Nope. The RadioButton is not updated at all .. because? Just step into RaiseCanExecuteChanged and inspect the value of the "CanExecuteChanged"-Event which is null
. Suprised? Why this all? Because there is a tutorial out there in the wild which does EXACTLY THAT (not from Microsoft) for Sample Usage for Microsoft Virtual Academy. So I'm talking about this sample, specifically these:
There's an MVA-Sample for using a SplitView with BackNavigation and stuff and this thing is not working as expected because why the hell should any bound command subscribe to a CanExecuteChanged-Event. Microsoft you failed hard here (or I did, because it's working and I didn't realize how to use it right, but hey, I have more).
After this I just took a step away from the UWP and tried it again later. Without SplitView. Just a simple view with buttons and EVENTHANDLERS, because Commands are not working correctly. So ... my girlfriend and I want to create an app which is NOT Accent-Colored, NOT Themed, and NOT the way, Microsoft wants Apps to be. Shouldn't be a problem at all, because WPF allows everything one might imagine, right? Fail. Things get even worse. How about some MSDN-Stuff here, because that are official samples and documentation and developer resources. Okay, let's check it out .. together. Want to know more about the {ThemeResource} markup extension
? This might be the page you're looking first. It's good and full of text and SAMPLES. Taking the first sample:
<!-- Default style for Windows.UI.Xaml.Controls.Button -->
<Style TargetType="Button">
<Setter Property="Background" Value="{ThemeResource ButtonBackgroundThemeBrush}" />
<Setter Property="Foreground" Value="{ThemeResource ButtonForegroundThemeBrush}"/>
You found out, that the Resource handling the background of a button is called "ButtonBackgroundThemeBrush" ... it's existing and NOT USED IN ANY UWP APP! Because hey, let's do SOMETHING ELSE WHICH IS EVEN BETTER!
There's even a sample resource dictionary entry for this:
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
...
<SolidColorBrush x:Key="ButtonBackgroundThemeBrush" Color="Transparent" />
<SolidColorBrush x:Key="ButtonForegroundThemeBrush" Color="#FFFFFFFF" />
Everything's good ... except that following will NOT WORK:
<Application>
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeResources>
<ResourceDictionary x:Key="Default">
<SolidColorBrush x:Key="ButtonBackgroundThemeBrush" Color="#000000" />
</ResourceDictionary>
</ResourceDictionary.ThemeResources>
</ResourceDictionary>
</Application.Resources>
</Application>
Now create a button and watch it not changing to a black background. It stays the theme's default. Because the used resources are DIFFERENT TO THOSE IN THIS ARTICLE. I searched around and got to a page called "Default control styles and templates" which has an entry to "Button styles and templates" which just states that
<Style TargetType="Button">
<Setter Property="Background" Value="{ThemeResource SystemControlBackgroundBaseLowBrush}" />
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseHighBrush}"/>
a button uses SystemControlBackgroundBaseLowBrush and SystemControlForegroundBaseHighBrush. WHY THE HELL ARE THOSE SAMPLES DIFFERENT!? Who is responsible for those samples and why are those not synchronized? If you're now overriding these everything works. Or not? Well, there's another thing which is annoying with the UWP:
AppBars
Yes, they're awesome ... if you're happy with single glyph icons which are just boring ... like any icon in any UWP app in the AppBar. Ever saw an app with a COLORFUL Icon in that bar? I didn't, because it's NOT WORKING. Why? Microsoft tells it for you in the remarks:
To use a BitmapIcon as the Icon for an AppBarButton, you specify the URI of an image file. The file that you use should be a solid image on a transparent background. The opaque parts of the image are shown in the Foreground color, and the transparent parts of the image are shown in the theme color specified in the AppBarButton template. Any color information in the image file is ignored.
Source: BitmapIcon class
You're amazing, colorful icon you wanted to use in the appbar just got blown off by Microsoft because WE DON'T WANT THAT! I'm talking about icons like this.
This is against Microsoft's guidelines and as consequence not working because fuck the developers who want to use this in another way we want it to be used.
The way Microsoft handles their UWP raises thoughts about "Why did they not include support for this?" and "Why don't they keep their documentation up-to-date?". Well I don't know and I would like to know why Microsoft has done those steps but I won't use the UWP for now until Microsoft allows developers to do what they want to do. Sure, I could create a custom control deriving from whatever control Microsoft has done for the AppBar but this doesn't work as I can't derive from IconElement without getting a CS1729. No possible way of using colored images in the AppBar. Shame on you Microsoft. ... why should I if it should have been Microsoft's reponsibility to provide those simple controls for everyone. Want to test what I told? Just add a simple control into your project and let it derive from IconElement. It won't work, although the IconElement should have a Constructor available but WTF it's not there. WHY!?
using System;
using Windows.Foundation;
using Windows.Foundation.Metadata;
using Windows.UI.Xaml.Media;
namespace Windows.UI.Xaml.Controls
{
[Composable, ContractVersion(typeof(UniversalApiContract), 65536u), MarshalingBehavior, Static(typeof(IIconElementStatics), 65536u, typeof(UniversalApiContract)), Threading, WebHostHidden]
public class IconElement : FrameworkElement, IIconElement
{
public extern Brush Foreground
{
get;
set;
}
public static extern DependencyProperty ForegroundProperty
{
get;
}
}
}
It's not even hiding anything. Well ... here is the sample class:
using Windows.UI.Xaml.Controls;
namespace Test
{
public class ImageIcon : IconElement // CS1729 on ImageIcon, IconElement does not have a constructor which accepts 0 parameters
{
}
}
Microsoft just showed me that WPF is ways more advanced than this prematurley release of a UWP. The idea is brilliant but the implementation has so many drawbacks that I'll stick to WPF.
TL;DR:
{ThemeResource} markup extension
, Button styles and templates)That's it from me for this rant. This just had to be posted.