Monday, August 14, 2017

WPF Tip #18 - Extended WPF Toolkit - Using the CheckedListBox Control

In today's tip, we're going to take a quick look at another control in the Extended WPF Toolkit, the CheckedListBox control.
CheckListBox_wpf
This control provides all of the functionality expected from a ListBox with the addition of checkboxes as a visual cue to indicate the selection state of each item.

Using the control is quite simple. Here's an example of the XAML required to display a CheckedListBox on a WPF window. It is binding to the same collection of Sentence objects used in Tip #17.

<tk:CheckListBox ItemsSource="{Binding Sentences, IsAsync=True}"
                  SelectedItem="{Binding SelectedSentence}"
                  Command="{Binding ItemSelectedCommand}"/>

ItemsSource binding to a collection to populate the list items. SelectedItem provides a way to find out the currently 'selected' item, the last item checked/unchecked. The Command will fire each time an item in the list is checked or unchecked, allowing ViewModel logic to act on this change.

Go give it a try in your WPF project. Happy coding!


del.icio.us Tags: ,

Wednesday, August 2, 2017

WPF Tip #17 - Extended WPF Toolkit - BusyIndicator Control

Welcome back to another Extended WPF Toolkit sample. In this sample, we'll take a look at the BusyIndicator control. This easy to use control provides a quick way to inform your users that a control or Window is currently being refreshed by a long-running process. This is a great way to both update your users of progress and keep them from interacting with the controls that are updating.
Here is a fun little example that wraps a WPF DataGrid in a BusyIndicator. This is the entirety of the XAML inside the root Grid on the application's main window.

<tk:BusyIndicator IsBusy="{Binding IsGridLoading}" BusyContent="Loading, please wait..." DisplayAfter="0">
     <DataGrid local:Commands.DataGridDoubleClickCommand="{Binding GridDoubleClickCommand}" ItemsSource="{Binding Sentences, IsAsync=True}"/>
</tk:BusyIndicator>

The DataGridDoubleClickCommand is a work-around to make the DataGrid's MouseDoubleClick event work with ICommand in when data binding in MVVM. More info here.

In the MainViewModel, there is an IsGridLoading boolean property that will be used to toggle the BusyIndicator on/off. The GridDoubleClickCommand invokes OnGridDoubleClicked() when the user double-clicks somewhere on the DataGrid at runtime. In this method, we're clearing and repopulating the Sentences ObservableCollection to which our DataGrid is bound. Notice the use of async/await. This is necessary to make the BusyIndicator appear. Without this, the UI will freeze during the update and never show the BusyIndicator control.

private async void OnGridDoubleClicked()
{
     //set the IsBusy before you start the thread
     IsGridLoading = true;
     RaisePropertyChanged(nameof(IsGridLoading));

     //no direct interaction with the UI is allowed from this method
     lock (_sentenceLock)
     {
         Sentences.Clear();
     }

     await BuildSentenceList(5, 15, 25, 50);

     //work has completed. you can now interact with the UI
     IsGridLoading = false;
     RaisePropertyChanged(nameof(IsGridLoading));
}

The _sentenceLock allows us to update our ObservableCollection on a background thread. You must also initialize the collection with this code in your constructor. This tells WPF to update the collection on the UI thread and which lock object to use. More info here.

BindingOperations.EnableCollectionSynchronization(Sentences, _sentenceLock);

Each row in the DataGrid will be populated with some random Bacon Ipsum data in the BuildSentenceList method. There is an API if you would like to remove the hard-coded array of words.

private async Task BuildSentenceList(int minWords, int maxWords,
     int minSentences, int maxSentences)

{
     var words = new[]{"bacon", "ipsum", "dolor", "tail", "amet", "swine",
         "chicken", "short", "loin", "jowl", "turkey", "ball", "tip",
         "beef", "shank", "rump", "t-bone", "ham", "porchetta", "filet",
         "pork", "jerky", "hock", "meatball", "biltong", "steak", "brisket"};

     var rand = new Random();
     int numSentences = rand.Next(maxSentences - minSentences)
                        + minSentences + 1;
     int numWords = rand.Next(maxWords - minWords) + minWords + 1;

     var result = new StringBuilder();

    Task t = Task.Run(() =>
     {
         for (int s = 0; s < numSentences; s++)
         {
             for (int w = 0; w < numWords; w++)
             {
                 if (w > 0)
                 {
                     result.Append(" ");
                 }
                 result.Append(words[rand.Next(words.Length)]);
             }
             result.Append(". ");

            lock (_sentenceLock)
                 Sentences.Add(new Sentence() {SentenceContent = result.ToString()});

            Thread.Sleep(100);

            result.Clear();
         }
     });

    await t;

}

When the code to populate the grid fires, thanks to the 100ms Thread.Sleep between each row generated, you can see the grid slowly populating with data behind the busy indicator.

baconipsum-loading

It's a great control. Just remember to update your data on a background thread!

Happy coding!


del.icio.us Tags: ,,,

Tuesday, July 25, 2017

WPF Tip #16 - Extended WPF Toolkit - ChildWindow & MessageBox Controls

Welcome to another Extended WPF Toolkit tip. In this tip, we will look at a couple of simple uses of the ChildWindow and MessageBox controls.

The ChildWindow control is a handy alternative to opening a separate dialog from the current application window, collecting a few pieces of data. The MessageBox operates similarly, but only presents some information and allows the user to respond, like any typical WinForm or WPF MessageBox.

In an MVVM world, opening another dialog typically either involves another View/ViewModel combination or a service to open a simple message/input dialog and return the result to the current View/ViewModel. This control is best used to replace a simple MessageBox service or when another View/ViewModel are being considered, but logically the data collected is limited and still belongs in the current ViewModel.

The use of the controls consists of a WindowContainer element containing one or more ChildWIndow and MessageBox elements. In this sample, the Window's main grid contains a WindowContainer with two ChildWindow elements and one MessageBox. They all begin with a Closed WindowState, and are opened with a button click. Here is a snippet of the view's XAML:

<StackPanel Grid.Row="2" Orientation="Horizontal" VerticalAlignment="Top">
     <Button Content="Toggle Window 1" Height="40" Width="120" Margin="2" Command="{Binding ButtonOneCommand}"/>
     <Button Content="Toggle Window 2" Height="40" Width="120" Margin="2" Command="{Binding ButtonTwoCommand}"/>
     <Button Content="Toggle Window 3" Height="40" Width="120" Margin="2" Click="ButtonBase_OnClick"/>
</StackPanel>

<tk:WindowContainer Grid.Row="2">
     <tk:ChildWindow WindowBackground="Blue"
                       Left="75"
                       Top="50"
                       Width="275"
                       Height="125" WindowState="{Binding WindowOneState}">
         <TextBlock Text="This is a child window" Padding="10"/>
     </tk:ChildWindow>

    <tk:ChildWindow WindowBackground="Green"
                       Left="175"
                       Top="125"
                       Width="275"
                       Height="125" WindowState="{Binding WindowTwoState}">
         <StackPanel>
             <TextBlock Text="This is a child window with a checkbox." Padding="10"/>
             <CheckBox Content="Check me!" Margin="8"/>
         </StackPanel>
     </tk:ChildWindow>

    <tk:MessageBox Caption="Toolkit Message" x:Name="SampleMsgBox"
                      Text="You have an alert!"/>

</tk:WindowContainer>

The MessageBox's button has a Click event handler to display the control:

private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
     SampleMsgBox.ShowMessageBox();
}

The two ChildWindows are opened with Commands attached to the first two buttons in our Window's ViewModel:

private void OnButtonOneClicked()
{
     if (WindowOneState == WindowState.Open)
         WindowOneState = WindowState.Closed;
     else
         WindowOneState = WindowState.Open;
    RaisePropertyChanged(nameof(WindowOneState));
}

public RelayCommand ButtonOneCommand { get; private set; }

private void OnButtonTwoClicked()
{
     if (WindowTwoState == WindowState.Open)
         WindowTwoState = WindowState.Closed;
     else
         WindowTwoState = WindowState.Open;
    RaisePropertyChanged(nameof(WindowTwoState));
}

public RelayCommand ButtonTwoCommand { get; private set; }

The ChildWindows can be closed again by using the close button on the element or by clicking it's Open button again. This second option is not available when the ChildWindow has IsModal set to true. You'll notice the WindowState property of the two ChildWindows is bound to the ViewModel. This two-way binding ensures the IF statements in the command methods will work properly regardless of how each window is closed.

Here is a look at the Window with the second ChildWindow open. This control contains a TextBlock and a CheckBox inside a StackPanel. The ChildWindow can contain only one direct child, like a Window or UserControl.

childwindows-wpf

There are tons or other uses for these controls and many properties I haven't touched on at all. Go explore them for yourself and download the toolkit today.

Happy coding!


del.icio.us Tags: ,,

Tuesday, July 18, 2017

WPF Tips Update - The Extended WPF Toolkit Has a New Home

Hello WPF fans! You may have noticed that I am a fan of Xceed's free Extended WPF Toolkit. I am actually a huge fan of free tools and productivity gains in general.

This week when I started working on WPF Tip #16, I was pleased to notice that Xceed has released v3.1 of the Extended WPF Toolkit and moved the source code from CodePlex to GitHub! The move to GitHub was, of course, motivated by Microsoft announcement that CodePlex is shutting down in the coming months.

Download, Star and Watch the Extended WPF Toolkit on GitHub

The changelog for v3.1.0 includes 37 fixes and enhancements. The Plus Edition snagged an additional 19 fixes and improvements including a Windows 10 theme, but Plus received v3.1.0 last year. It pays to pay, it seems.

wpftoolkit-github

Learn all about the toolkit on GitHub or check out WPF Tip #11. This tip was my introductory post about the controls. And be sure to stay tuned for more WPF Tips about these controls and lots more!

Happy coding!


Saturday, July 8, 2017

WPF Tip #15 - Extended WPF Toolkit - The DataGrid Control

Tip #15 is a quick detour. We'll get back to using the CalculatorUpDown control in Tip #16.

If you are looking for a quick, free way to upgrade your application's DataGrid look and behavior, the Extended WPF Toolkit includes a DataGrid which provides a significant upgrade over the WPF DataGrid in the .NET Framework.

Here is a simple Grid with a standard DataGrid in the first row and the Toolkit's DataGridControl in the second row. Both are bound to the same ItemsSource, an ObservableCollection of three fictional videos.
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
   
    <DataGrid ItemsSource="{Binding Path=MediaCatalog, IsAsync=True}"/>

    <xcdg:DataGridControl Grid.Row="1" ItemsSource="{Binding Path=MediaCatalog, IsAsync=True}"/>
</Grid>
This is what we get when running the application.


With no customization, there's a much cleaner look. You also get an improved editing experience. When clicking into the PurchaseDate column, the grid detects that the underlying data type is DateTime, and provides a Date Picker to update the value.


For more information about features available in the control, go check out the CodePlex site (migrating soon).

Happy Coding!

Friday, June 23, 2017

WPF Tip #14 - Extended WPF Toolkit - The CalculatorUpDown Control

In this tip, we will continue to explore the free controls available in the Extended WPF Toolkit Community Edition.

The CalculatorUpDown control is a hybrid of sorts. It takes the functionality of an up/down numeric control and adds a dropdown button. Clicking on the dropdown button pops open a Calculator control. The output of the calculator displays in its window as well as in the text portion of the CalculatorUpDown.

Let's take a simple example of the control's use and walk through a few of the properties and how they impact the control's functionality. Here is the XAML:
<tk:CalculatorUpDown Grid.Row="1" Height="32" VerticalAlignment="Top" FormatString="C2" Watermark="Calculate!" Increment=".01" Maximum="9999.99" Minimum="0.01"/>
Upon first launching the window, this is how the control looks:
calc0
There is a numeric up/down with a watermark reading "Calculate!" and a dropdown button to open the calculator control. Let's click the up button to start.
calc0a
The Increment property is set to ".01" and the FormatString is "C2", so our value has incremented to $0.01 or 1 U.S. cent. You'll notice that the down button is now disabled because the Minimum property is currently equal to the control's Value.

Now to explore the calculator control a little. Click the dropdown button and enter a calculation.
calc1
As you use the calculator, you will notice that the calculator window updates as you enter and calculate values, but the textbox of the control only updates to show results of calculations. If the calculated value falls outside the Maximum or Minimum properties, it will default to those Max/Min values.

There was no code required on our part to add this rich functionality or our application, only the XAML markup seen above. You certainly could take this a step further by adding some data binding and leveraging other properties that the control exposes to do things like allowing the user to change the number format.

Happy coding!

del.icio.us Tags: ,,

Tuesday, June 13, 2017

WPF Tip #13 - Extended WPF Toolkit - ColorCanvas with MVVMLight, Binding and EventToCommand

In Tip #12, we used a ColorCanvas control in a SplitButton to change the Background color of the SplitButton using an event in the code behind. In this tip, we will do the same thing with data binding and commands using MVVMLight.

The first step is to add the MVVMLight NuGet package to the project:

image

Adding the package will add and update several classes in the project. We now have a MainViewModel and ViewModelLocator inside a ViewModel folder. The locator class exposes each ViewModel in the project so they can be bound in your views. To make the locator discoverable to the views in the project, MVVMLight added a new resource to the App.xaml:

<Application.Resources>
   <ResourceDictionary>
     <vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" xmlns:vm="clr-namespace:WpfApp1.ViewModel" />
   </ResourceDictionary>
</Application.Resources>

Now lets start with the changes we have to make. First remove the event handler from the MainWindow.xaml.cs. The next step is to replace that functionality with some decoupled logic in the MainViewModel. Two things are needed:

  • A ButtonBrush property to bind the SplitButton's Background property.
  • A ChangeBrushColorCommand RelayCommand to bind to the ColorChanged event of the ColorCanvas.

An OnColorChanged method is added to do the work needed when the RelayCommand is invoked. These are hooked up in the constructor, along with some default colors for the SplitButton. I made the button different colors at design-time and runtime as a way to validate that my binding code isn't broken. Here's the complete MainViewModel implementation:

public class MainViewModel : ViewModelBase
{
     public MainViewModel()
     {
         if (IsInDesignMode)
         {
             ButtonBrush = new SolidColorBrush(Colors.LightGray);
         }
         else
         {
             ButtonBrush = new SolidColorBrush(Colors.LightSkyBlue);

            ChangeBrushColorCommand = new RelayCommand<Color?>(OnColorChanged);
         }
     }

    public SolidColorBrush ButtonBrush { get; set; }

    internal void OnColorChanged(Color? p_Param)
     {
         if (p_Param.HasValue)
         {
             ButtonBrush = new SolidColorBrush(p_Param.Value);
         }
         else
         {
             ButtonBrush = new SolidColorBrush(Colors.Transparent);
         }

        RaisePropertyChanged(nameof(ButtonBrush));
     }

    public RelayCommand<Color?> ChangeBrushColorCommand { get; private set; }
}

The next thing we need is a converter class to convert the event args of the ColorChanged event to the Color? parameter type of our RelayCommand. SelectedColorChangedToColorConverter implements the IEventArgsConverter in MvvmLight. It's similar to value converters you may have created for data binding in WPF (see WPF Tip #4).

public class SelectedColorChangedToColorConverter : IEventArgsConverter
{
     public object Convert(object value, object parameter)
     {
         var args = (RoutedPropertyChangedEventArgs<Color?>)value;

        return args.NewValue ?? Colors.Transparent;
     }
}

Finally, lets make a few changes to the MainWindow view.

Set the DataContext for the Window using the ViewModelLocator:

DataContext="{Binding Main, Source={StaticResource Locator}}"

Add the converter to the Window.Resources:

<Window.Resources>
     <ResourceDictionary>
         <local:SelectedColorChangedToColorConverter x:Key="SelectedColorChangedToColorConverter" />
     </ResourceDictionary>
</Window.Resources>

Update the SplitButton to bind its Background property and the ColorCanvas to use the MVVMLight EventToCommand trigger, hooking up our RelayCommand through the converter.

<tk:SplitButton Content="Pick a Color" Height="32" Width="120" Background="{Binding ButtonBrush}">
     <tk:SplitButton.DropDownContent>
         <tk:ColorCanvas x:Name="MainColorCanvas">
             <i:Interaction.Triggers>
                 <i:EventTrigger EventName="SelectedColorChanged">
                     <command:EventToCommand Command="{Binding ChangeBrushColorCommand, Mode=OneWay}"
                                             EventArgsConverter="{StaticResource SelectedColorChangedToColorConverter}"
                                             PassEventArgsToCommand="True"/>
                 </i:EventTrigger>
             </i:Interaction.Triggers>
         </tk:ColorCanvas>
     </tk:SplitButton.DropDownContent>
</tk:SplitButton>

That's it!. Same functionality, but now with better separation between view and code.

Happy coding! (P.S. - I hope to get some proper code formatting hooked up to Blogger soon… or maybe a new blogging platform. Thanks for bearing with me.)