Bad Entropy
Wednesday, 4 February 2015
Friday, 22 November 2013
A GitHub Client with WPF and ReactiveUI Part 2
The Views
In Part 1 we set up the view models for our simple Github client. In this part, we’ll create the views to display these view models.
First of all comes MainWindow.xaml. It’s pretty simple, consisting of only a ReactiveUI ViewModelViewHost control. We briefly mentioned this control last time but to recap, it’s basically an analog of WPF’s ContentControl. You pass a ViewModel into it’s ViewModel property and it tries to look up a corresponding view to display based on the type of the view model.
<Window x:Class="ReactiveGitHubClient.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:xaml="clr-namespace:ReactiveUI.Xaml;assembly=ReactiveUI.Xaml" Title="GitHub with ReactiveUI"> <xaml:ViewModelViewHost HorizontalContentAlignment="Center" VerticalContentAlignment="Center" ViewModel="{Binding Content}"/> </Window>
As you can see, we add a binding to the Content property we implemented in MainWindowViewModel last time. Note that here we’re using a standard XAML binding. ReactiveUI provides its own binding system which is better than WPF’s in many ways, but we’ll get to that later.
Now in the code behind, we simply set the DataContext as we would in any other MVVM application.
public partial class MainWindow : Window { public MainWindow() { this.InitializeComponent(); this.DataContext = new MainWindowViewModel(); } }
Now we move onto LoginView.xaml:
<UserControl x:Class="ReactiveGitHubClient.Views.LoginView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:wag="clr-namespace:WpfAutoGrid;assembly=WpfAutoGrid"> <StackPanel> <wag:AutoGrid Columns="Auto,180" ChildMargin="2"> <Label>User name</Label> <TextBox Text="{Binding UserName, UpdateSourceTrigger=PropertyChanged}"/> <Label>Password</Label> <TextBox Name="Password"/> </wag:AutoGrid> <Button Name="okButton" HorizontalAlignment="Center" MinWidth="80" Margin="4"> OK </Button> </StackPanel> </UserControl>
Here we’re using the super-useful WpfAutoGrid which makes laying out a grid a little less verbose. There’s a NuGet package for it which makes adding it to a project a breeze. We add a Label and TextBox for the UserName and Password and an OK button. You’ll note here that we’re only binding the UserName TextBox in XAML; we’ll use ReactiveUI’s binding to bind the Password TextBox and OK button so you can see a demo of how that works.
ReactiveUI bindings are set up in the code-behind so let’s take a look at that now:
public partial class LoginView : UserControl, IViewFor<LoginViewModel> { public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register( "ViewModel", typeof(LoginViewModel), typeof(LoginView)); public LoginView() { this.InitializeComponent(); // We need to bind the ViewModel property to the DataContext in order to be able to // use WPF Bindings. Let's use WPF bindings for the UserName property. this.WhenAnyValue(x => x.ViewModel).BindTo(this, x => x.DataContext); // And let's use ReactiveUI bindings for the Password property and the Login command. // Note that we don't need to explicitly pass the control we're binding to here for // Password, as the control is named the same as the property on the view model and // ReactiveUI is intelligent enough to realise we want to bind the Text property. this.Bind(this.ViewModel, x => x.Password); // For the LoginCommand on the other hand, we must use a one way binding and explicitly // specify the control and the property being bound to. this.OneWayBind(this.ViewModel, x => x.LoginCommand, x => x.okButton.Command); } public LoginViewModel ViewModel { get { return (LoginViewModel)this.GetValue(ViewModelProperty); } set { this.SetValue(ViewModelProperty, value); } } object IViewFor.ViewModel { get { return this.ViewModel; } set { this.ViewModel = (LoginViewModel)value; } } }
The first thing to notice is that LoginView implements the IViewFor<LoginViewModel> interface. This is the mechanism by which ViewModelViewHost finds a view for a view model. To implement this interface we need to provide a ViewModel property which we implement here as a standard dependency property. The generic IViewFor<T> interface derives from the non-generic IViewFor interface - whose ViewModel property returns a simple object - so we need to also implement that using an explicit interface member.
Now let’s take a look at the 3 lines in the constructor after the standard InitializeComponent call:
this.WhenAnyValue(x => x.ViewModel).BindTo(this, x => x.DataContext);
This first line is necessary because we’re using WPF’s binding system for the UserName TextBox. You’ll notice that IViewFor uses the ViewModel property to specify the bound ViewModel rather than DataContext. This is because DataContext is a XAML-family-specific property and ReactiveUI supports other UI frameworks. Because we want to use WPF’s binding here, we need to keep the two in sync. This line binds LoginViewModel.ViewModel to DataContext so that whenever the former is changed, the change is automatically reflected in the latter. You’ll come to realise that this type of binding can be very useful.
// And let's use ReactiveUI bindings for the Password property and the Login command. // Note that we don't need to explicitly pass the control we're binding to here for // Password, as the control is named the same as the property on the view model and // ReactiveUI is intelligent enough to realise we want to bind the Text property. this.Bind(this.ViewModel, x => x.Password);
This line sets up the ReactiveUI binding we’ve heard so much about. I’ve left the comment in there because I think it just about covers everything.
Note that for this automatic binding to work, the bound control has to be named/capitalized the same as the property that it’s bound to. Unfortunately this violates the .NET coding guidelines as controls are added to the class as fields, which should use camelCase capitalization. This is only one of a few places where ReactiveUI violates .NET conventions, which is something I personally find very irritating, being a stickler for code style! (Note the use of StyleCop in the GitHub project! ;) However, the next line shows us the workaround.
this.OneWayBind(this.ViewModel, x => x.LoginCommand, x => x.okButton.Command);
Here we’re binding Button.Command to LoginViewModel.LoginCommand. Because the LoginCommand property is read-only, we set up the binding to be one-way from the ViewModel to the Button. You can see in the third parameter to OneWayBind that the Button instance and the property on the Button are explicitly supplied; we can also supply this third parameter to the two-way Bind method if we want/need to.
If you run the program now, you’ll find that ReactiveUI throws a NullReferenceException:
An unhandled exception of type 'System.NullReferenceException' occurred in ReactiveUI.Xaml.dll
This isn’t a very helpful message, but what’s happening is that ReactiveUI can’t find a view for the initial value of MainWindowViewModel.Content, which is a LoginViewModel. We need to tell ReactiveUI that LoginView is the view for LoginViewModel. For now, we’ll do this in the App class’ constructor in App.xaml.cs:
public App() { RxApp.MutableResolver.Register(() => new LoginView(), typeof(IViewFor<LoginViewModel>)); }
When ViewModelViewHost tries to look up a view for a view model type TViewModel, it uses ReactiveUI’s dependency resolver to try to find a concrete implementation of IViewFor<TViewModel>. Here we’re using the default dependency resolver to say “when an IViewFor<LoginViewModel> is needed, create a new LoginView”. The ReactiveUI dependency resolver can be overridden with the DI framework of your choice, but for the purposes of this demo we’re going to stick to ReactiveUI’s default.
If you now run the application, you should see a basic login screen:
You’ll notice that the OK button is disabled until a user name and password is entered, as we specified in our view model.
Next time, we’ll make it actually do something, but for now you can find the source on GitHub.
A GitHub Client with WPF and ReactiveUI Part 1
The View Models
Other posts in this series:
ReactiveUI is an awesome framework, but there’s a serious lack of documentation and examples on the net. So why not build a simple Github client using WPF and ReactiveUI.
You can find the source here on GitHub.
First of all, let’s display a login page. We’ll want this page to take up the whole of the application’s main window while the user is logging in and then disappear to be replaced by the real content.
(Hey, I didn’t say it’d be pretty)
In WPF, there’s a super useful control for contextually displaying views called ContentControl. You’d set a property on a view model, bind the ContentControl.Content property to this and depending on what type was assigned to the view model property, the ContentControl could select (via Data Templates) the correct view. The equivalent in ReactiveUI is the ViewModelViewHost control and it works somewhat similarly (but without Data Templates).
So lets start with the MainWindowViewModel:
public class MainWindowViewModel : ReactiveObject { private ReactiveObject content; public MainWindowViewModel() { // Initially show the login view. this.content = new LoginViewModel(); } public ReactiveObject Content { get { return this.content; } private set { this.RaiseAndSetIfChanged(ref this.content, value); } } }
You’ll note that the view model inherits from ReactiveObject. This is the base class for – surprise - reactive objects in ReactiveUI. The RaiseAndSetIfChanged extension method helps keep the INotifyPropertyChanged boilerplate to a minimum. We have a Content property that is initially set to an instance of LoginViewModel:
public class LoginViewModel : ReactiveObject { private string userName; private string password; public LoginViewModel() { // The login command is only enabled when both a user name and password have been // entered. this.LoginCommand = new ReactiveCommand( this.WhenAny( x => x.UserName, x => x.Password, (userName, password) => !string.IsNullOrWhiteSpace(userName.Value) && !string.IsNullOrWhiteSpace(password.Value))); } public string UserName { get { return this.userName; } set { this.RaiseAndSetIfChanged(ref this.userName, value); } } public string Password { get { return this.password; } set { this.RaiseAndSetIfChanged(ref this.password, value); } } public ReactiveCommand LoginCommand { get; private set; } }
In LoginViewModel we have the expected UserName and Password properties, together with a LoginCommand. This is the command that will be executed when the user clicks the OK button on the login form.
ReactiveUI’s magical WhenAny extension method sets up an Observable to ensure that the command should only be enabled when both a user name and password have been entered.
Pretty straightforward MVVM so far. In Part 2 we’ll create the views.
Friday, 17 February 2012
An Accordion Panel in WPF
I recently had need of an Accordian control, such as that found in JQuery UI.
I decided to implement this as a simple Panel. This AccordianPanel can contain any controls, but has special behaviour for child Expander controls: It will only allow a single one to be expanded at any one time.
public AccordianPanel() { AddHandler(Expander.ExpandedEvent, new RoutedEventHandler(ChildExpanded)); }Starting with the constructor, we register a new event handler for the Expander.ExpandedEvent:
void ChildExpanded(object sender, RoutedEventArgs e) { foreach (UIElement child in InternalChildren) { Expander expander = FindExpander(child); if (expander != null && expander != e.OriginalSource) { expander.IsExpanded = false; } } }When we detect a child Expander's Expanded state has changed, we look for any other child Expanders and unexpand them. The FindExpander function allows for the Expander to be parented:
Expander FindExpander(UIElement e) { while (e != null && !(e is Expander)) { if (VisualTreeHelper.GetChildrenCount(e) == 1) { e = VisualTreeHelper.GetChild(e, 0) as UIElement; } else { e = null; } } return (Expander)e; }Finally, we implement MeasureOverride:
protected override Size MeasureOverride(Size availableSize) { double requiredHeight = 0; double resizableHeight = 0; foreach (UIElement child in InternalChildren) { child.Measure(availableSize); requiredHeight += child.DesiredSize.Height; if (CanResize(child)) { resizableHeight += child.DesiredSize.Height; } } if (requiredHeight > availableSize.Height) { double pixelsToLose = requiredHeight - availableSize.Height; foreach (UIElement child in InternalChildren) { double height = child.DesiredSize.Height; if (CanResize(child)) { height -= (child.DesiredSize.Height / resizableHeight) * pixelsToLose; child.Measure(new Size(availableSize.Width, height)); } } } return base.MeasureOverride(availableSize); }And ArrangeOverride:
protected override Size ArrangeOverride(Size finalSize) { double totalHeight = 0; double resizableHeight = 0; foreach (UIElement child in Children) { totalHeight += child.DesiredSize.Height; if (CanResize(child)) { resizableHeight += child.DesiredSize.Height; } } double pixelsToLose = totalHeight - finalSize.Height; double y = 0; foreach (UIElement child in InternalChildren) { double height = child.DesiredSize.Height; if (pixelsToLose > 0 && CanResize(child)) { height -= (child.DesiredSize.Height / resizableHeight) * pixelsToLose; } child.Arrange(new Rect(0, y, finalSize.Width, height)); y += height; } return base.ArrangeOverride(finalSize); }Plus the small Utility function CanResize:
bool CanResize(UIElement e) { Expander expander = FindExpander(e); return expander != null && expander.IsExpanded; }
And that's it! I initially had problems with the fact that the layout for this type of panel requires two passes, which doesn't immediately seem to be supported by WPF. However, following a question on the ever-reliable StackOverflow, it seems it can in fact be done.
You can even make it animated using something like the AnimatedPanel from here.
Saturday, 11 February 2012
WebP from .Net
I recently had the need to convert images to Google's .webp format from .net. There is a .net port of the WebP library available, but it didn't seem to work for me and it seems to be no longer maintained.
So P/Invoke it was. Here's the code I used:
Bitmap source = new Bitmap("Desert.jpg"); BitmapData data = source.LockBits( new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); IntPtr unmanagedData; int size = WebPEncodeBGR(data.Scan0, source.Width, source.Height, data.Stride, 85, out unmanagedData); byte[] managedData = new byte[size]; Marshal.Copy(unmanagedData, managedData, 0, size); File.WriteAllBytes("Desert.webp", managedData); Marshal.FreeHGlobal(unmanagedData); // Doesn't work. How do we free the unmanaged data?
As you can see, all goes swimmingly until it comes to freeing the unmanaged memory allocated by the WebP library. The memory is allocated by malloc() and needs to be freed by calling free(), however you have no access to this from .net.
My solution was to modify the WebP library to provide a WebPFree function, which simply calls the C free(). Calling this instead of Marshal.FreeHGlobal fixes the memory leak.
I've included my modifed WebP binaries as part of a sample project here.
Sunday, 5 February 2012
MVVM and Multiple Selection – Part IV - DataGrid
Download the source for this article.
Previous posts in this series:
- http://grokys.blogspot.com/2010/07/mvvm-and-multiple-selection-part-iii.html
- http://grokys.blogspot.com/2010/07/mvvm-and-multiple-selection-part-ii.html
- http://grokys.blogspot.com/2010/07/mvvm-and-multiple-selection-part-i.html
Please note, this is WPF only - I don't use Silverlight!
It's been a while since my previous post in this series, MVVM and Multiple Selection – Part III. In the comments to that post, I had a number of people asking about the DataGrid control, however by that point I was no longer in WPF-land and didn't have the time or inclination to investigate properly.
Recently though, I needed to track multiple selections on a DataGrid and as the commenters pointed out, it didn't work. The problem came down to the method I was using to track changes to the ItemsSource property on a control:
static MultiSelect() { Selector.ItemsSourceProperty.OverrideMetadata(typeof(Selector), new FrameworkPropertyMetadata(ItemsSourceChanged)); }
This doesn't work for DataGrid as the ItemsSource metadata on that control is already overridden, so I needed to find a different approach.
Here's what I came up with:
static void IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) { Selector selector = sender as Selector; bool enabled = (bool)e.NewValue; if (selector != null) { DependencyPropertyDescriptor itemsSourceProperty = DependencyPropertyDescriptor.FromProperty(Selector.ItemsSourceProperty, typeof(Selector)); IMultiSelectCollectionView collectionView = selector.ItemsSource as IMultiSelectCollectionView; if (enabled) { if (collectionView != null) collectionView.AddControl(selector); itemsSourceProperty.AddValueChanged(selector, ItemsSourceChanged); } else { if (collectionView != null) collectionView.RemoveControl(selector); itemsSourceProperty.RemoveValueChanged(selector, ItemsSourceChanged); } } }Instead of overriding the property metadata globally for all Selectors, I attach a listener to the PropertyDescriptor. Now, the only problem here is that the event handler for PropertyDescriptor changed events are plain old EventHandler delegates, so by the time the property has changed we can't get hold of the old IMultiSelectCollectionView in order to remove the control from it. I get round this by storing all attached controls in a static Dictionary.
This leaves us the problem that we're going to be keeping controls alive when after they're gone, but that's a post for another day. In the meantime, download the code!
Thursday, 19 May 2011
Automatic AJAX-ification of forms
Do Somethingthen in your script, you can simply use this call:
$('a.open-dialog').click(function () { var title = $(this).attr('title'); $('<div><div>).dialog({ buttons: { OK: function () { $('form', this).submit(); $(this).dialog('close'); }, Cancel: function () { $(this).dialog('close'); } }, modal: true, title: title }).load($(this).attr('href')); return false; });This will display a dialog with the contents and title of the link instead of navigating to a new page. Make sure that in your controller you call Request.IsAjaxRequest() and if this returns true, return a Partial containing only the content to show in the dialog.