Show / Hide Table of Contents

Movies.WPF Project

In this section, we will work on Movies.WPF project, a WPF application to display and manage a database of movies.

Project Setup

Make the following changes to Movies.WPF project:

Step 1. Add project reference Movies to this project.

Step 2. Add NuGet package DevZest.Data.WPF to this project.

Step 3. Add the following assembly level attribute to Properties/AssemblyInfo.cs (or My Project/AssemblyInfo.vb if you're using VB.Net):

  • C#
  • VB.Net
using DevZest.Data.Presenters;
using Movies.WPF;
...
[assembly: ResourceIdRelativeTo(typeof(App))]
Imports DevZest.Data.Presenters
...
<Assembly: ResourceIdRelativeTo(GetType(Application))>
Note

If you cannot find AssemblyInfo.vb in Solution Explorer, make sure you have enabled "Show all files" on the toolbar of the Solution Explorer.

Step 4. Add Movies.mdf and Movies_log.ldf to this project as linking, by right clicking Movies.WPF project in Solution Explorer tool window, then click context menu item "Add" -> "Existing Item...", select All Files(.) from right bottom combobox, then navigate to LocalDb subfolder of Movies.DbDesign project, select file Movies.mdf and Movies_log.ldf:

image

Click the little dropdown arrow next to the Add button and select Add as Link, instead of copying the file into the project directory, Visual Studio will create a link to the original:

image

Select these two files, then right click and select context menu item Properties, make sure Copy to Output Directory is set to Copy Always:

image

App/Application

Change App.xaml.cs(Application.xaml.vb if you're using VB.Net) to:

  • C#
  • VB.Net
using System;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows;

namespace Movies.WPF
{
    public partial class App : Application
    {
        private static readonly string ConnectionString = GetConnectionString();

        private static string GetConnectionString()
        {
            string mdfFilename = "Movies.mdf";
            string outputFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            string attachDbFilename = Path.Combine(outputFolder, mdfFilename);
            return string.Format(@"Data Source=(localdb)\MSSQLLocalDB;AttachDbFilename=""{0}"";Integrated Security=True", attachDbFilename);
        }

        private static Db CreateDb()
        {
            return new Db(App.ConnectionString);
        }

        public static async Task ExecuteAsync(Func<Db, Task> func)
        {
            using (var db = CreateDb())
            {
                await func(db);
            }
        }

        public static async Task<T> ExecuteAsync<T>(Func<Db, Task<T>> func)
        {
            using (var db = CreateDb())
            {
                return await func(db);
            }
        }

    }
}
Imports System.IO
Imports System.Reflection

Class Application
    Private Shared ReadOnly ConnectionString As String = GetConnectionString()

    Private Shared Function GetConnectionString() As String
        Dim mdfFilename As String = "Movies.mdf"
        Dim outputFolder As String = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)
        Dim attachDbFilename As String = Path.Combine(outputFolder, mdfFilename)
        Return String.Format("Data Source=(localdb)\MSSQLLocalDB;AttachDbFilename=""{0}"";Integrated Security=True", attachDbFilename)
    End Function

    Private Shared Function CreateDb() As Db
        Return New Db(ConnectionString)
    End Function

    Public Shared Async Function ExecuteAsync(func As Func(Of Db, Task)) As Task
        Using db = CreateDb()
            Await func(db)
        End Using
    End Function

    Public Shared Async Function ExecuteAsync(Of T)(func As Func(Of Db, Task(Of T))) As Task(Of T)
        Using db = CreateDb()
            Return Await func(db)
        End Using
    End Function
End Class

MovieDetailWindow

Add class file MovieDetailWindow.Presenter.cs (MovieDetailWindow.Presenter.vb if you're using VB.Net):

  • C#
  • VB.Net
using DevZest.Data.Presenters;
using System.Threading.Tasks;

namespace Movies.WPF
{
    partial class MovieDetailWindow
    {
        private sealed class Presenter : DataPresenter<Movie>
        {
            private const string LABEL_FORMAT = "{0}:";

            protected override void BuildTemplate(TemplateBuilder builder)
            {
                var title = _.Title.BindToTextBox();
                var releaseDate = _.ReleaseDate.BindToDatePicker();
                var genre = _.Genre.BindToTextBox();
                var price = _.Price.BindToTextBox();

                builder
                    .GridColumns("Auto", "*")
                    .GridRows("Auto", "Auto", "Auto", "Auto")
                    .AddBinding(0, 0, _.Title.BindToLabel(title, LABEL_FORMAT))
                    .AddBinding(0, 1, _.ReleaseDate.BindToLabel(releaseDate, LABEL_FORMAT))
                    .AddBinding(0, 2, _.Genre.BindToLabel(genre, LABEL_FORMAT))
                    .AddBinding(0, 3, _.Price.BindToLabel(price, LABEL_FORMAT))
                    .AddBinding(1, 0, title)
                    .AddBinding(1, 1, releaseDate)
                    .AddBinding(1, 2, genre)
                    .AddBinding(1, 3, price);
            }

            public int ID
            {
                get { return _.ID[0].Value; }
            }

            public bool IsNew
            {
                get { return ID < 1; }
            }

            public Task SaveToDbAsync()
            {
                if (IsNew)
                    return App.ExecuteAsync(db => db.Movie.InsertAsync(DataSet));
                else
                    return App.ExecuteAsync(db => db.Movie.UpdateAsync(DataSet));
            }
        }
    }
}
Imports DevZest.Data.Presenters

Partial Class MovieDetailWindow
    Private NotInheritable Class Presenter
        Inherits DataPresenter(Of Movie)
        Private Const LABEL_FORMAT As String = "{0}:"

        Protected Overrides Sub BuildTemplate(builder As TemplateBuilder)
            Dim title = Entity.Title.BindToTextBox()
            Dim releaseDate = Entity.ReleaseDate.BindToDatePicker()
            Dim genre = Entity.Genre.BindToTextBox()
            Dim price = Entity.Price.BindToTextBox()

            builder.GridColumns("Auto", "*") _
                .GridRows("Auto", "Auto", "Auto", "Auto") _
                .AddBinding(0, 0, Entity.Title.BindToLabel(title, LABEL_FORMAT)) _
                .AddBinding(0, 1, Entity.ReleaseDate.BindToLabel(releaseDate, LABEL_FORMAT)) _
                .AddBinding(0, 2, Entity.Genre.BindToLabel(genre, LABEL_FORMAT)) _
                .AddBinding(0, 3, Entity.Price.BindToLabel(price, LABEL_FORMAT)) _
                .AddBinding(1, 0, title) _
                .AddBinding(1, 1, releaseDate) _
                .AddBinding(1, 2, genre) _
                .AddBinding(1, 3, price)
        End Sub

        Public ReadOnly Property ID() As Integer
            Get
                Return Entity.ID(0).Value
            End Get
        End Property

        Public ReadOnly Property IsNew() As Boolean
            Get
                Return ID < 1
            End Get
        End Property

        Public Function SaveToDbAsync() As Task
            If IsNew Then
                Return Application.ExecuteAsync(Function(db) db.Movie.InsertAsync(DataSet))
            Else
                Return Application.ExecuteAsync(Function(db) db.Movie.UpdateAsync(DataSet))
            End If
        End Function
    End Class
End Class

Add window MovieDetailWindow.xaml to project. Change MovieDetailWindow.xaml to:

  • C#
  • VB.Net
<Window x:Class="Movies.WPF.MovieDetailWindow"
        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Movies.WPF"
        xmlns:dz="http://schemas.devzest.com/data/windows"
        mc:Ignorable="d"
        Title="Movie" Height="230" Width="300" WindowStartupLocation="CenterOwner">
    <Window.Resources>
        <Style TargetType="Label">
            <Setter Property="Margin" Value="5,5,0,5" />
            <Setter Property="HorizontalAlignment" Value="Right" />
        </Style>
        <Style TargetType="TextBox">
            <Setter Property="VerticalAlignment" Value="Center" />
            <Setter Property="Margin" Value="5,0,5,0" />
        </Style>
        <Style TargetType="DatePicker">
            <Setter Property="VerticalAlignment" Value="Center" />
            <Setter Property="Margin" Value="5,0,5,0" />
        </Style>
        <Style TargetType="Button">
            <Setter Property="Margin" Value="5" />
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <dz:DataView x:Name="_dataView" Background="White" Margin="5" />
        <UniformGrid Width="180" Columns="2" HorizontalAlignment="Right" VerticalAlignment="Bottom" Grid.Row="1">
            <Button Content="OK" IsDefault="True" Command="{x:Static local:MovieDetailWindow+Commands.Submit}" />
            <Button Content="Cancel" IsCancel="True" />
        </UniformGrid>
    </Grid>
</Window>
<Window x:Class="MovieDetailWindow"
        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Movies.WPF"
        xmlns:dz="http://schemas.devzest.com/data/windows"
        mc:Ignorable="d"
        Title="Movie" Height="230" Width="300" WindowStartupLocation="CenterOwner">
    <Window.Resources>
        <Style TargetType="Label">
            <Setter Property="Margin" Value="5,5,0,5" />
            <Setter Property="HorizontalAlignment" Value="Right" />
        </Style>
        <Style TargetType="TextBox">
            <Setter Property="VerticalAlignment" Value="Center" />
            <Setter Property="Margin" Value="5,0,5,0" />
        </Style>
        <Style TargetType="DatePicker">
            <Setter Property="VerticalAlignment" Value="Center" />
            <Setter Property="Margin" Value="5,0,5,0" />
        </Style>
        <Style TargetType="Button">
            <Setter Property="Margin" Value="5" />
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <dz:DataView x:Name="_dataView" Background="White" Margin="5" />
        <UniformGrid Width="180" Columns="2" HorizontalAlignment="Right" VerticalAlignment="Bottom" Grid.Row="1">
            <Button Content="OK" IsDefault="True" Command="{x:Static local:MovieDetailWindow+Commands.Submit}" />
            <Button Content="Cancel" IsCancel="True" />
        </UniformGrid>
    </Grid>
</Window>

Change code behind MovieDetailWindow.xaml.cs (MovieDetailWindow.xaml.vb if you're using VB.Net) to:

  • C#
  • VB.Net
using DevZest.Data;
using System.Diagnostics;
using System.Windows;
using System.Windows.Input;

namespace Movies.WPF
{
    public partial class MovieDetailWindow : Window
    {
        public static class Commands
        {
            public static readonly RoutedCommand Submit = new RoutedCommand(nameof(Submit), typeof(MovieDetailWindow));
        }

        public static bool Show(DataSet<Movie> data, Window ownerWindow)
        {
            return new MovieDetailWindow().ShowDialog(data, ownerWindow);
        }

        public MovieDetailWindow()
        {
            InitializeComponent();
            CommandBindings.Add(new CommandBinding(Commands.Submit, ExecSubmit, CanExecSubmit));
        }

        private Presenter _presenter;

        private async void ExecSubmit(object sender, ExecutedRoutedEventArgs e)
        {
            if (_presenter.IsEditing)
                _presenter.CurrentRow.EndEdit();

            if (!_presenter.SubmitInput())
                return;

            await _presenter.SaveToDbAsync();
            DialogResult = true;
            Close();
        }

        private void CanExecSubmit(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = _presenter.CanSubmitInput;
        }

        private bool ShowDialog(DataSet<Movie> data, Window ownerWindow)
        {
            Debug.Assert(data.Count == 1);
            _presenter = new Presenter();
            _presenter.Show(_dataView, data);
            Owner = ownerWindow;
            Title = _presenter.IsNew ? "New Movie" : string.Format("Movie: {0}", _presenter.ID);
            return ShowDialog().Value;
        }
    }
}
Imports DevZest.Data

Partial Public Class MovieDetailWindow
    Public NotInheritable Class Commands
        Private Sub New()
        End Sub

        Public Shared ReadOnly Submit As New RoutedCommand(NameOf(Submit), GetType(MovieDetailWindow))
    End Class

    Public Overloads Shared Function Show(data As DataSet(Of Movie), ownerWindow As Window) As Boolean
        Return New MovieDetailWindow().ShowDialog(data, ownerWindow)
    End Function

    Public Sub New()
        InitializeComponent()
        CommandBindings.Add(New CommandBinding(Commands.Submit, AddressOf ExecSubmit, AddressOf CanExecSubmit))
    End Sub

    Private _presenter As Presenter

    Private Async Sub ExecSubmit(sender As Object, e As ExecutedRoutedEventArgs)
        If _presenter.IsEditing Then
            _presenter.CurrentRow.EndEdit()
        End If

        If Not _presenter.SubmitInput() Then
            Return
        End If

        Await _presenter.SaveToDbAsync()
        DialogResult = True
        Close()
    End Sub

    Private Sub CanExecSubmit(sender As Object, e As CanExecuteRoutedEventArgs)
        e.CanExecute = _presenter.CanSubmitInput
    End Sub

    Private Overloads Function ShowDialog(data As DataSet(Of Movie), ownerWindow As Window) As Boolean
        Debug.Assert(data.Count = 1)
        _presenter = New Presenter()
        _presenter.Show(_dataView, data)
        Owner = ownerWindow
        Title = If(_presenter.IsNew, "New Movie", String.Format("Movie: {0}", _presenter.ID))
        Return ShowDialog().Value
    End Function

End Class

MainWindow

Add class file MainWindow.Presenter.cs (MainWindow.Presenter.vb if you're using VB.Net):

  • C#
  • VB.Net
using DevZest.Data;
using DevZest.Data.Presenters;
using DevZest.Data.Views;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Controls;

namespace Movies.WPF
{
    partial class MainWindow
    {
        public static class Styles
        {
            public static readonly StyleId CheckBox = new StyleId(typeof(MainWindow));
            public static readonly StyleId LeftAlignedTextBlock = new StyleId(typeof(MainWindow));
            public static readonly StyleId RightAlignedTextBlock = new StyleId(typeof(MainWindow));
        }

        public sealed class Presenter : DataPresenter<Movie>
        {
            public interface IFilter
            {
                string Text { get; set; }
            }

            public Presenter(IFilter filter)
            {
                _filter = filter;
            }

            private readonly IFilter _filter;

            private Task<DataSet<Movie>> LoadDataAsync(CancellationToken ct)
            {
                return App.ExecuteAsync(db => db.GetMoviesAsync(_filter.Text, ct));
            }

            public void ShowAsync(DataView dataView)
            {
                ShowAsync(dataView, LoadDataAsync);
            }

            public Task RefreshAsync(bool clearFiler)
            {
                if (clearFiler)
                    _filter.Text = null;
                return RefreshAsync(LoadDataAsync);
            }

            protected override void BuildTemplate(TemplateBuilder builder)
            {
                builder.GridColumns("20", "*", "Auto", "Auto", "Auto")
                .GridRows("Auto", "Auto")
                .Layout(Orientation.Vertical)
                .WithFrozenTop(1)
                .AddBinding(0, 0, this.BindToCheckBox().WithStyle(Styles.CheckBox))
                .AddBinding(1, 0, _.Title.BindToColumnHeader())
                .AddBinding(2, 0, _.ReleaseDate.BindToColumnHeader())
                .AddBinding(3, 0, _.Genre.BindToColumnHeader())
                .AddBinding(4, 0, _.Price.BindToColumnHeader())
                .AddBinding(0, 1, _.BindToCheckBox().WithStyle(Styles.CheckBox))
                .AddBinding(1, 1, _.Title.BindToHyperlink(Commands.Open).WithStyle(Styles.LeftAlignedTextBlock))
                .AddBinding(2, 1, _.ReleaseDate.BindToTextBlock("{0:d}").WithStyle(Styles.RightAlignedTextBlock))
                .AddBinding(3, 1, _.Genre.BindToTextBlock().WithStyle(Styles.LeftAlignedTextBlock))
                .AddBinding(4, 1, _.Price.BindToTextBlock("{0:C}").WithStyle(Styles.RightAlignedTextBlock));
            }
        }
    }
}
Imports System.Threading
Imports DevZest.Data
Imports DevZest.Data.Presenters
Imports DevZest.Data.Views

Partial Class MainWindow
    Public NotInheritable Class Styles
        Public Shared ReadOnly CheckBox As New StyleId(GetType(MainWindow))
        Public Shared ReadOnly LeftAlignedTextBlock As New StyleId(GetType(MainWindow))
        Public Shared ReadOnly RightAlignedTextBlock As New StyleId(GetType(MainWindow))
    End Class

    Public NotInheritable Class Presenter
        Inherits DataPresenter(Of Movie)

        Public Interface IFilter
            Property Text() As String
        End Interface

        Public Sub New(filter As IFilter)
            _filter = filter
        End Sub

        Private ReadOnly _filter As IFilter

        Private Function LoadDataAsync(ct As CancellationToken) As Task(Of DataSet(Of Movie))
            Return Application.ExecuteAsync(Function(db) db.GetMoviesAsync(_filter.Text, ct))
        End Function

        Public Overloads Sub ShowAsync(dataView As DataView)
            ShowAsync(dataView, AddressOf LoadDataAsync)
        End Sub

        Public Overloads Function RefreshAsync(clearFilder As Boolean) As Task
            If clearFilder Then
                _filter.Text = Nothing
            End If
            Return RefreshAsync(AddressOf LoadDataAsync)
        End Function

        Protected Overrides Sub BuildTemplate(builder As TemplateBuilder)
            builder.GridColumns("20", "*", "Auto", "Auto", "Auto") _
                .GridRows("Auto", "Auto") _
                .Layout(Orientation.Vertical) _
                .WithFrozenTop(1) _
                .AddBinding(0, 0, Me.BindToCheckBox().WithStyle(Styles.CheckBox)) _
                .AddBinding(1, 0, Entity.Title.BindToColumnHeader()) _
                .AddBinding(2, 0, Entity.ReleaseDate.BindToColumnHeader()) _
                .AddBinding(3, 0, Entity.Genre.BindToColumnHeader()) _
                .AddBinding(4, 0, Entity.Price.BindToColumnHeader()) _
                .AddBinding(0, 1, Entity.BindToCheckBox().WithStyle(Styles.CheckBox)) _
                .AddBinding(1, 1, Entity.Title.BindToHyperlink(Commands.Open).WithStyle(Styles.LeftAlignedTextBlock)) _
                .AddBinding(2, 1, Entity.ReleaseDate.BindToTextBlock("{0:d}").WithStyle(Styles.RightAlignedTextBlock)) _
                .AddBinding(3, 1, Entity.Genre.BindToTextBlock().WithStyle(Styles.LeftAlignedTextBlock)) _
                .AddBinding(4, 1, Entity.Price.BindToTextBlock("{0:C}").WithStyle(Styles.RightAlignedTextBlock))
        End Sub
    End Class
End Class

Add resource dictionary MainWindow.Styles.xaml:

  • C#
  • VB.Net
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:Movies.WPF">
    <Style x:Key="DefaultTextBlock" TargetType="TextBlock">
        <Setter Property="Padding" Value="2" />
        <Setter Property="VerticalAlignment" Value="Center" />
    </Style>
    <Style x:Key="{x:Static local:MainWindow+Styles.CheckBox}" TargetType="CheckBox">
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="VerticalAlignment" Value="Center" />
    </Style>
    <Style x:Key="{x:Static local:MainWindow+Styles.LeftAlignedTextBlock}" TargetType="TextBlock" BasedOn="{StaticResource DefaultTextBlock}">
        <Setter Property="TextAlignment" Value="Left" />
    </Style>
    <Style x:Key="{x:Static local:MainWindow+Styles.RightAlignedTextBlock}" TargetType="TextBlock" BasedOn="{StaticResource DefaultTextBlock}">
        <Setter Property="TextAlignment" Value="Right" />
    </Style>
</ResourceDictionary>
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:Movies.WPF">
    <Style x:Key="DefaultTextBlock" TargetType="TextBlock">
        <Setter Property="Padding" Value="2" />
        <Setter Property="VerticalAlignment" Value="Center" />
    </Style>
    <Style x:Key="{x:Static local:MainWindow+Styles.CheckBox}" TargetType="CheckBox">
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="VerticalAlignment" Value="Center" />
    </Style>
    <Style x:Key="{x:Static local:MainWindow+Styles.LeftAlignedTextBlock}" TargetType="TextBlock" BasedOn="{StaticResource DefaultTextBlock}">
        <Setter Property="TextAlignment" Value="Left" />
    </Style>
    <Style x:Key="{x:Static local:MainWindow+Styles.RightAlignedTextBlock}" TargetType="TextBlock" BasedOn="{StaticResource DefaultTextBlock}">
        <Setter Property="TextAlignment" Value="Right" />
    </Style>
</ResourceDictionary>

Change MainWindow.xaml to:

  • C#
  • VB.Net
<Window x:Class="Movies.WPF.MainWindow"
        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:dz="http://schemas.devzest.com/data/windows"
        xmlns:local="clr-namespace:Movies.WPF"
        mc:Ignorable="d"
        Title="Movies.WPF" Height="300" Width="500" WindowStartupLocation="CenterScreen">
    <DockPanel>
        <ToolBar DockPanel.Dock="Top">
            <Button Command="{x:Static local:MainWindow+Commands.New}">New</Button>
            <Separator />
            <Button Command="{x:Static local:MainWindow+Commands.Delete}">Delete</Button>
            <Separator />
            <Button Command="{x:Static local:MainWindow+Commands.Refresh}">Refresh</Button>
            <Separator />
            <TextBox x:Name="_textBoxSearch" Width="150" />
            <Button Command="{x:Static local:MainWindow+Commands.Refresh}">Search</Button>
            <Separator />
        </ToolBar>
        <dz:DataView x:Name="_dataView" />
    </DockPanel>
</Window>
<Window x:Class="MainWindow"
        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:dz="http://schemas.devzest.com/data/windows"
        xmlns:local="clr-namespace:Movies.WPF"
        mc:Ignorable="d"
        Title="Movies.WPF" Height="300" Width="500" WindowStartupLocation="CenterScreen">
    <DockPanel>
        <ToolBar DockPanel.Dock="Top">
            <Button Command="{x:Static local:MainWindow+Commands.New}">New</Button>
            <Separator />
            <Button Command="{x:Static local:MainWindow+Commands.Delete}">Delete</Button>
            <Separator />
            <Button Command="{x:Static local:MainWindow+Commands.Refresh}">Refresh</Button>
            <Separator />
            <TextBox x:Name="_textBoxSearch" Width="150" />
            <Button Command="{x:Static local:MainWindow+Commands.Refresh}">Search</Button>
            <Separator />
        </ToolBar>
        <dz:DataView x:Name="_dataView" />
    </DockPanel>
</Window>

Change code behind MainWindow.xaml.cs (MainWindow.xaml.vb if you're using VB.Net) to:

  • C#
  • VB.Net
using DevZest.Data;
using System.Diagnostics;
using System.Windows;
using System.Windows.Input;

namespace Movies.WPF
{
    public partial class MainWindow : Window, MainWindow.Presenter.IFilter
    {
        public static class Commands
        {
            public static RoutedUICommand New { get { return ApplicationCommands.New; } }
            public static RoutedUICommand Open { get { return ApplicationCommands.Open; } }
            public static RoutedUICommand Delete { get { return ApplicationCommands.Delete; } }
            public static RoutedUICommand Refresh { get { return NavigationCommands.Refresh; } }
        }

        private readonly Presenter _presenter;

        public MainWindow()
        {
            InitializeComponent();
            InitializeCommandBindings();
            _presenter = new Presenter(this);
            _presenter.ShowAsync(_dataView);
        }

        string Presenter.IFilter.Text
        {
            get { return _textBoxSearch.Text; }
            set { _textBoxSearch.Text = value; }
        }

        private void InitializeCommandBindings()
        {
            CommandBindings.Add(new CommandBinding(Commands.New, New));
            CommandBindings.Add(new CommandBinding(Commands.Open, Open, CanOpen));
            CommandBindings.Add(new CommandBinding(Commands.Delete, Delete, CanDelete));
            CommandBindings.Add(new CommandBinding(Commands.Refresh, Refresh, CanRefresh));
        }

        private void ShowMovieDetailWindow(DataSet<Movie> movie)
        {
            Debug.Assert(movie.Count == 1);
            var result = MovieDetailWindow.Show(movie, this);
            if (result)
                Refresh(true);
        }

        private void New(object sender, ExecutedRoutedEventArgs e)
        {
            var movie = DataSet<Movie>.Create();
            movie.AddRow();
            ShowMovieDetailWindow(movie);
        }

        private Movie _
        {
            get { return _presenter._; }
        }

        private async void Open(object sender, ExecutedRoutedEventArgs e)
        {
            var ID = _presenter.CurrentRow.GetValue(_.ID).Value;
            var movie = await App.ExecuteAsync(db => db.GetMovieAsync(ID));
            ShowMovieDetailWindow(movie);
        }

        private void CanOpen(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = _presenter.CurrentRow != null;
        }

        private async void Delete(object sender, ExecutedRoutedEventArgs e)
        {
            var selectedRows = _presenter.SelectedRows;
            var json = _presenter.DataSet.Filter(JsonFilter.PrimaryKeyOnly).ToJsonString(_presenter.SelectedDataRows, false);
            var keys = DataSet<Movie.Key>.ParseJson(json);
            await App.ExecuteAsync(db => db.Movie.DeleteAsync(keys, (s, _) => s.Match(_)));
            Refresh(false);
        }

        private void CanDelete(object sender, CanExecuteRoutedEventArgs e)
        {
            var selectedRows = _presenter.SelectedRows;
            e.CanExecute = selectedRows != null && selectedRows.Count > 0;
        }

        private void Refresh(object sender, ExecutedRoutedEventArgs e)
        {
            Refresh(false);
        }

        private void Refresh(bool clearFilter)
        {
            _presenter.RefreshAsync(clearFilter);
        }

        private void CanRefresh(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = _presenter.DataSet != null;
        }
    }
}
Imports DevZest.Data

Partial Class MainWindow
    Implements MainWindow.Presenter.IFilter

    Public NotInheritable Class Commands
        Private Sub New()
        End Sub

        Public Shared ReadOnly Property [New]() As RoutedUICommand
            Get
                Return ApplicationCommands.[New]
            End Get
        End Property

        Public Shared ReadOnly Property Open() As RoutedUICommand
            Get
                Return ApplicationCommands.Open
            End Get
        End Property

        Public Shared ReadOnly Property Delete() As RoutedUICommand
            Get
                Return ApplicationCommands.Delete
            End Get
        End Property
        Public Shared ReadOnly Property Refresh() As RoutedUICommand
            Get
                Return NavigationCommands.Refresh
            End Get
        End Property
    End Class

    Private ReadOnly _presenter As Presenter

    Public Sub New()
        InitializeComponent()
        InitializeCommandBindings()
        _presenter = New Presenter(Me)
        _presenter.ShowAsync(_dataView)
    End Sub

    Private Property Text() As String Implements Presenter.IFilter.Text
        Get
            Return _textBoxSearch.Text
        End Get
        Set
            _textBoxSearch.Text = Value
        End Set
    End Property

    Private Sub InitializeCommandBindings()
        CommandBindings.Add(New CommandBinding(Commands.[New], AddressOf [New]))
        CommandBindings.Add(New CommandBinding(Commands.Open, AddressOf Open, AddressOf CanOpen))
        CommandBindings.Add(New CommandBinding(Commands.Delete, AddressOf Delete, AddressOf CanDelete))
        CommandBindings.Add(New CommandBinding(Commands.Refresh, AddressOf Refresh, AddressOf CanRefresh))
    End Sub

    Private Sub ShowMovieDetailWindow(movie As DataSet(Of Movie))
        Debug.Assert(movie.Count = 1)
        Dim result = MovieDetailWindow.Show(movie, Me)
        If result Then
            Refresh(True)
        End If
    End Sub

    Private Sub [New](sender As Object, e As ExecutedRoutedEventArgs)
        Dim movie = DataSet(Of Movie).Create()
        movie.AddRow()
        ShowMovieDetailWindow(movie)
    End Sub

    Private ReadOnly Property Entity() As Movie
        Get
            Return _presenter.Entity
        End Get
    End Property

    Private Async Sub Open(sender As Object, e As ExecutedRoutedEventArgs)
        Dim ID = _presenter.CurrentRow.GetValue(Entity.ID).Value
        Dim movie = Await Application.ExecuteAsync(Function(db) db.GetMovieAsync(ID))
        ShowMovieDetailWindow(movie)
    End Sub

    Private Sub CanOpen(sender As Object, e As CanExecuteRoutedEventArgs)
        e.CanExecute = _presenter.CurrentRow IsNot Nothing
    End Sub

    Private Async Sub Delete(sender As Object, e As ExecutedRoutedEventArgs)
        Dim selectedRows = _presenter.SelectedRows
        Dim json = _presenter.DataSet.Filter(JsonFilter.PrimaryKeyOnly).ToJsonString(_presenter.SelectedDataRows, False)
        Dim keys = DataSet(Of Movie.Key).ParseJson(json)
        Await Application.ExecuteAsync(Function(db) db.Movie.DeleteAsync(keys, Function(s, t) s.Match(t)))
        Refresh(False)
    End Sub

    Private Sub CanDelete(sender As Object, e As CanExecuteRoutedEventArgs)
        Dim selectedRows = _presenter.SelectedRows
        e.CanExecute = selectedRows IsNot Nothing AndAlso selectedRows.Count > 0
    End Sub

    Private Sub Refresh(sender As Object, e As ExecutedRoutedEventArgs)
        Refresh(False)
    End Sub

    Private Sub Refresh(clearFilter As Boolean)
        _presenter.RefreshAsync(clearFilter)
    End Sub

    Private Sub CanRefresh(sender As Object, e As CanExecuteRoutedEventArgs)
        e.CanExecute = _presenter.DataSet IsNot Nothing
    End Sub
End Class

Run

Press F5 to run project Movies.WPF. You should have a working WPF application to display and manage a database of movies:

image

  • Improve this Doc
Back to top Copyright © Weifen Luo | DevZest