initial load

This commit is contained in:
2022-08-22 15:56:12 +02:00
commit d3b7ff17be
62 changed files with 2694 additions and 0 deletions

View File

@ -0,0 +1,94 @@
<Application x:Class="YouTubeViewers.WPF.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:YouTubeViewers.WPF">
<Application.Resources>
<ResourceDictionary>
<SolidColorBrush x:Key="BorderPrimary" Color="#1c1c1c" />
<Style x:Key="PageHeader" TargetType="TextBlock" >
<Setter Property="FontSize" Value="32" />
</Style>
<Style TargetType="Button">
<Setter Property="Foreground" Value="White" />
<Setter Property="Background" Value="#547AFF" />
<Setter Property="Padding" Value="20 8" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="TextBlock.TextAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Margin="{TemplateBinding Margin}"
Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
CornerRadius="3" >
<ContentPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="Background.(SolidColorBrush.Color)"
To="#2a3d82" Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="Background.(SolidColorBrush.Color)"
To="#547AFF" Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.7" />
</Trigger>
</Style.Triggers>
</Style>
<Style
x:Key="ButtonSecondary"
BasedOn="{StaticResource {x:Type Button}}"
TargetType="Button">
<Setter Property="Foreground" Value="Black" />
<Setter Property="Background" Value="#E1E1E1" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="Background.(SolidColorBrush.Color)"
To="#c2c2c2" Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="Background.(SolidColorBrush.Color)"
To="#E1E1E1" Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@ -0,0 +1,78 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using YouTubeViewers.Domain.Commands;
using YouTubeViewers.Domain.Queries;
using YouTubeViewers.EntityFramework;
using YouTubeViewers.EntityFramework.Commands;
using YouTubeViewers.EntityFramework.Queries;
using YouTubeViewers.WPF.Stores;
using YouTubeViewers.WPF.ViewModels;
namespace YouTubeViewers.WPF;
public partial class App : Application
{
private readonly ModalNavigationStore _modalNavigationStore;
private readonly YouTubeViewersDbContextFactory _youTubeViewersDbContextFactory;
private readonly IGetAllYouTubeViewersQuery _getAllYouTubeViewersQuery;
private readonly ICreateYouTubeViewerCommand _createYouTubeViewerCommand;
private readonly IUpdateYouTubeViewerCommand _updateYouTubeViewerCommand;
private readonly IDeleteYouTubeViewerCommand _deleteYouTubeViewerCommand;
private readonly YouTubeViewersStore _youTubeViewersStore;
private readonly SelectedYouTubeViewerStore _selectedYouTubeViewerStore;
public App()
{
string connectionString = "Data Source=YouTubeViewers.db";
_modalNavigationStore = new ModalNavigationStore();
_youTubeViewersDbContextFactory = new YouTubeViewersDbContextFactory(
new DbContextOptionsBuilder()
.UseSqlite(connectionString)
.Options
);
_getAllYouTubeViewersQuery = new GetAllYouTubeViewersQuery(_youTubeViewersDbContextFactory);
_createYouTubeViewerCommand = new CreateYouTubeViewerCommand(_youTubeViewersDbContextFactory);
_updateYouTubeViewerCommand = new UpdateYouTubeViewerCommand(_youTubeViewersDbContextFactory);
_deleteYouTubeViewerCommand = new DeleteYouTubeViewerCommand(_youTubeViewersDbContextFactory);
_youTubeViewersStore = new YouTubeViewersStore(
_getAllYouTubeViewersQuery,
_createYouTubeViewerCommand,
_updateYouTubeViewerCommand,
_deleteYouTubeViewerCommand
);
_selectedYouTubeViewerStore = new SelectedYouTubeViewerStore(_youTubeViewersStore);
}
protected override void OnStartup(StartupEventArgs e)
{
using(YouTubeViewersDbContext context = _youTubeViewersDbContextFactory.Create())
{
//context.Database.EnsureCreated();
context.Database.Migrate();
}
YouTubeViewersViewModel youTubeViewersViewModel = YouTubeViewersViewModel.LoadViewModel(
_youTubeViewersStore,
_selectedYouTubeViewerStore,
_modalNavigationStore);
MainWindow = new MainWindow()
{
DataContext = new MainViewModel(_modalNavigationStore, youTubeViewersViewModel)
};
MainWindow.Show();
base.OnStartup(e);
}
}

View File

@ -0,0 +1,10 @@
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]

View File

@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YouTubeViewers.Domain.Models;
using YouTubeViewers.WPF.Stores;
using YouTubeViewers.WPF.ViewModels;
namespace YouTubeViewers.WPF.Commands
{
public class AddYouTubeViewerCommand : AsyncCommandBase
{
private readonly AddYouTubeViewerViewModel _addYouTubeViewerViewModel;
private readonly YouTubeViewersStore _youTubeViewersStore;
private readonly ModalNavigationStore _modalNavigationStore;
public AddYouTubeViewerCommand(AddYouTubeViewerViewModel addYouTubeViewerViewModel, YouTubeViewersStore youTubeViewersStore, ModalNavigationStore modalNavigationStore)
{
_addYouTubeViewerViewModel = addYouTubeViewerViewModel;
_youTubeViewersStore = youTubeViewersStore;
_modalNavigationStore = modalNavigationStore;
}
public override async Task ExecuteAsync(object? parameter)
{
YouTubeViewerDetailsFormViewModel formViewModel = _addYouTubeViewerViewModel.YouTubeViewerDetailsFormViewModel;
formViewModel.IsSubmitting = true;
YouTubeViewer youTubeViewer = new YouTubeViewer(
Guid.NewGuid(),
formViewModel.UserName,
formViewModel.IsSubScribed,
formViewModel.IsMember);
// Add YouTubeViewer to database
try
{
await _youTubeViewersStore.Add(youTubeViewer);
_modalNavigationStore.Close();
}
catch (Exception)
{
throw;
}
finally
{
formViewModel.IsSubmitting = false;
}
}
}
}

View File

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace YouTubeViewers.WPF.Commands
{
public abstract class AsyncCommandBase : CommandBase
{
private bool _isExecuting;
public bool IsExecuting
{
get { return _isExecuting; }
set { _isExecuting = value;
OnCanExecuteChanged();
}
}
public override bool CanExecute(object? parameter)
{
return !_isExecuting && base.CanExecute(parameter);
}
public override async void Execute(object? parameter)
{
IsExecuting = true;
try
{
await ExecuteAsync(parameter);
}
catch (Exception) { }
finally
{
IsExecuting = false;
}
}
public abstract Task ExecuteAsync(object? parameter);
}
}

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YouTubeViewers.WPF.Stores;
namespace YouTubeViewers.WPF.Commands
{
public class CloseModalCommand : CommandBase
{
private readonly ModalNavigationStore _modalNavigationStore;
public CloseModalCommand(ModalNavigationStore modalNavigationStore)
{
_modalNavigationStore = modalNavigationStore;
}
public override void Execute(object? parameter)
{
_modalNavigationStore.Close();
}
}
}

View File

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace YouTubeViewers.WPF.Commands
{
public abstract class CommandBase : ICommand
{
public event EventHandler? CanExecuteChanged;
public virtual bool CanExecute(object? parameter)
{
return true;
}
public abstract void Execute(object? parameter);
protected virtual void OnCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
}

View File

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YouTubeViewers.Domain.Models;
using YouTubeViewers.WPF.Stores;
using YouTubeViewers.WPF.ViewModels;
namespace YouTubeViewers.WPF.Commands
{
public class DeleteYouTubeViewerCommand : AsyncCommandBase
{
private readonly YouTubeViewersListingItemViewModel _youTubeViewersListingItemViewModel;
private readonly YouTubeViewersStore _youTubeViewersStore;
public DeleteYouTubeViewerCommand(
YouTubeViewersListingItemViewModel youTubeViewersListingItemViewModel,
YouTubeViewersStore youTubeViewersStore)
{
_youTubeViewersListingItemViewModel = youTubeViewersListingItemViewModel;
_youTubeViewersStore = youTubeViewersStore;
}
public override async Task ExecuteAsync(object? parameter)
{
YouTubeViewer youTubeViewer = _youTubeViewersListingItemViewModel.YouTubeViewer;
try
{
await _youTubeViewersStore.Delete(youTubeViewer.Id);
}
catch (Exception)
{
throw;
}
}
}
}

View File

@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YouTubeViewers.Domain.Models;
using YouTubeViewers.WPF.Stores;
using YouTubeViewers.WPF.ViewModels;
namespace YouTubeViewers.WPF.Commands
{
public class EditYouTubeViewerCommand : AsyncCommandBase
{
private readonly EditYouTubeViewerViewModel _editYouTubeViewerViewModel;
private readonly YouTubeViewersStore _youTubeViewersStore;
private readonly ModalNavigationStore _modalNavigationStore;
public EditYouTubeViewerCommand(EditYouTubeViewerViewModel editYouTubeViewerViewModel, YouTubeViewersStore youTubeViewersStore, ModalNavigationStore modalNavigationStore)
{
_editYouTubeViewerViewModel = editYouTubeViewerViewModel;
_youTubeViewersStore = youTubeViewersStore;
_modalNavigationStore = modalNavigationStore;
}
public override async Task ExecuteAsync(object? parameter)
{
YouTubeViewerDetailsFormViewModel formViewModel = _editYouTubeViewerViewModel.YouTubeViewerDetailsFormViewModel;
formViewModel.IsSubmitting = true;
YouTubeViewer youTubeViewer = new YouTubeViewer(
_editYouTubeViewerViewModel.YouTubeViewerId,
formViewModel.UserName,
formViewModel.IsSubScribed,
formViewModel.IsMember);
// Add YouTubeViewer to database
try
{
await _youTubeViewersStore.Update(youTubeViewer);
_modalNavigationStore.Close();
}
catch (Exception)
{
throw;
}
finally
{
formViewModel.IsSubmitting = false;
}
}
}
}

View File

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YouTubeViewers.WPF.Stores;
using YouTubeViewers.WPF.ViewModels;
namespace YouTubeViewers.WPF.Commands
{
public class LoadYouTubeViewersCommand : AsyncCommandBase
{
private readonly YouTubeViewersViewModel _youTubeViewersViewModel;
private readonly YouTubeViewersStore _youTubeViewersStore;
public LoadYouTubeViewersCommand(YouTubeViewersViewModel youTubeViewersViewModel, YouTubeViewersStore youTubeViewersStore)
{
_youTubeViewersViewModel = youTubeViewersViewModel;
_youTubeViewersStore = youTubeViewersStore;
}
public override async Task ExecuteAsync(object? parameter)
{
_youTubeViewersViewModel.IsLoading = true;
try
{
await _youTubeViewersStore.Load();
}
catch (Exception)
{
throw;
}
finally
{
_youTubeViewersViewModel.IsLoading = false;
}
}
}
}

View File

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using YouTubeViewers.WPF.Stores;
using YouTubeViewers.WPF.ViewModels;
namespace YouTubeViewers.WPF.Commands
{
public class OpenAddYouTubeViewerCommand : CommandBase
{
private readonly YouTubeViewersStore _youTubeViewersStore;
private readonly ModalNavigationStore _modalNavigationStore;
public OpenAddYouTubeViewerCommand(YouTubeViewersStore youTubeViewersStore, ModalNavigationStore modalNavigationStore)
{
_youTubeViewersStore = youTubeViewersStore;
_modalNavigationStore = modalNavigationStore;
}
public override void Execute(object? parameter)
{
AddYouTubeViewerViewModel addYouTubeViewerViewModel = new AddYouTubeViewerViewModel(_youTubeViewersStore, _modalNavigationStore);
_modalNavigationStore.CurrentViewModel = addYouTubeViewerViewModel;
}
}
}

View File

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YouTubeViewers.Domain.Models;
using YouTubeViewers.WPF.Stores;
using YouTubeViewers.WPF.ViewModels;
namespace YouTubeViewers.WPF.Commands
{
public class OpenEditYouTubeViewerCommand: CommandBase
{
private readonly YouTubeViewersListingItemViewModel _youTubeViewersListingItemViewModel;
private readonly YouTubeViewersStore _youTubeViewersStore;
private readonly ModalNavigationStore _modalNavigationStore;
public OpenEditYouTubeViewerCommand(
YouTubeViewersListingItemViewModel youTubeViewersListingItemViewModel,
YouTubeViewersStore youTubeViewersStore,
ModalNavigationStore modalNavigationStore)
{
_youTubeViewersListingItemViewModel = youTubeViewersListingItemViewModel;
_youTubeViewersStore = youTubeViewersStore;
_modalNavigationStore = modalNavigationStore;
}
public override void Execute(object? parameter)
{
YouTubeViewer youTubeViewer = _youTubeViewersListingItemViewModel.YouTubeViewer;
EditYouTubeViewerViewModel editYouTubeViewerViewModel = new EditYouTubeViewerViewModel(youTubeViewer,_youTubeViewersStore, _modalNavigationStore);
_modalNavigationStore.CurrentViewModel = editYouTubeViewerViewModel;
}
}
}

View File

@ -0,0 +1,70 @@
<UserControl x:Class="YouTubeViewers.WPF.Components.YouTubeViewerDetailsForm"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:YouTubeViewers.WPF.Components"
xmlns:custom="clr-namespace:LoadingSpinnerControl;assembly=LoadingSpinnerControl"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid Grid.IsSharedSizeScope="True">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" SharedSizeGroup="Label"/>
<ColumnDefinition Width="*" MaxWidth="250"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Username" FontWeight="Bold" />
<TextBox Margin="20 0 0 0"
Grid.Column="1"
Text="{Binding UserName, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
<Grid Grid.Row="1" Margin="0 20 0 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" SharedSizeGroup="Label"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Is Subscribed?" FontWeight="Bold" />
<CheckBox Margin="20 0 0 0"
Grid.Column="1"
VerticalAlignment="Center"
IsChecked="{Binding IsSubScribed}" />
</Grid>
<Grid Grid.Row="2" Margin="0 20 0 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" SharedSizeGroup="Label"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Is Member?" FontWeight="Bold" />
<CheckBox Margin="20 0 0 0"
Grid.Column="1"
VerticalAlignment="Center"
IsChecked="{Binding IsMember}" />
</Grid>
</Grid>
<WrapPanel Grid.Row="1" Margin="0 30 0 0">
<Button Margin="0 0 5 0"
Command="{Binding SubmitCommand}"
Content="Submit"
IsEnabled="{Binding CanSubmit}"/>
<Button Margin="0 0 5 0" Command="{Binding CancelCommand}"
Content="Cancel"
Style="{StaticResource ButtonSecondary}"/>
<custom:LoadingSpinner IsLoading="{Binding IsSubmitting}" Diameter="25" Thickness="2"/>
</WrapPanel>
</Grid>
</UserControl>

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace YouTubeViewers.WPF.Components
{
/// <summary>
/// Interaction logic for YouTubeViewerDetailsForm.xaml
/// </summary>
public partial class YouTubeViewerDetailsForm : UserControl
{
public YouTubeViewerDetailsForm()
{
InitializeComponent();
}
}
}

View File

@ -0,0 +1,73 @@
<UserControl x:Class="YouTubeViewers.WPF.Components.YouTubeViewersDetails"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:YouTubeViewers.WPF.Components"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Border Padding="20" BorderBrush="{StaticResource BorderPrimary}"
BorderThickness="1" CornerRadius="5"
SnapsToDevicePixels="True">
<Grid>
<Grid>
<Grid.Style>
<Style TargetType="Grid">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding HasSelectedYouTubeViewer}" Value="false">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<TextBlock Grid.Row="0"
Text="Please select a YouTube Viewer to see their details." />
</Grid>
<Grid Grid.IsSharedSizeScope="True">
<Grid.Style>
<Style TargetType="Grid">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding HasSelectedYouTubeViewer}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0"
FontSize="24"
Text="{Binding UserName}" />
<Grid Grid.Row="1" Margin="0 20 0 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" SharedSizeGroup="Label"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Is Subscribed?" FontWeight="Bold" />
<TextBlock Margin="20 0 0 0"
Grid.Column="1"
Text="{Binding IsSubScribedDisplay}" />
</Grid>
<Grid Grid.Row="2" Margin="0 20 0 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" SharedSizeGroup="Label"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Is Member?" FontWeight="Bold" />
<TextBlock Margin="20 0 0 0"
Grid.Column="1"
Text="{Binding IsMemberDisplay}" />
</Grid>
</Grid>
</Grid>
</Border>
</Grid>
</UserControl>

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace YouTubeViewers.WPF.Components
{
/// <summary>
/// Interaction logic for YouTubeViewersDetails.xaml
/// </summary>
public partial class YouTubeViewersDetails : UserControl
{
public YouTubeViewersDetails()
{
InitializeComponent();
}
}
}

View File

@ -0,0 +1,41 @@
<UserControl x:Class="YouTubeViewers.WPF.Components.YouTubeViewersListing"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:YouTubeViewers.WPF.Components"
xmlns:custom="clr-namespace:DropdownMenuControl;assembly=DropdownMenuControl"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Border
BorderBrush="{StaticResource BorderPrimary}"
BorderThickness="1"
CornerRadius="5"
SnapsToDevicePixels="True">
<Grid>
<Grid.OpacityMask>
<VisualBrush Visual="{Binding ElementName=border}"/>
</Grid.OpacityMask>
<Border
x:Name="border"
Background="White"
CornerRadius="5"/>
<ListView BorderThickness="0"
ItemsSource="{Binding YouTubeViewersListingItemViewModels}"
SelectedItem="{Binding SelectedYouTubeViewerListingItemViewModel}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<local:YouTubeViewersListingItem/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Border>
</Grid>
</UserControl>

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace YouTubeViewers.WPF.Components
{
/// <summary>
/// Interaction logic for YouTubeViewersListing.xaml
/// </summary>
public partial class YouTubeViewersListing : UserControl
{
public YouTubeViewersListing()
{
InitializeComponent();
}
}
}

View File

@ -0,0 +1,80 @@
<UserControl x:Class="YouTubeViewers.WPF.Components.YouTubeViewersListingItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:YouTubeViewers.WPF.Components"
xmlns:custom="clr-namespace:DropdownMenuControl;assembly=DropdownMenuControl"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Border Padding="10" TextBlock.FontSize="16">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding UserName}"/>
<custom:DropdownMenu x:Name="dropdown" Grid.Column="1" Margin="10 0 0 0">
<Border
Background="White"
BorderThickness="1"
BorderBrush="Gray">
<StackPanel MinWidth="125">
<StackPanel.Resources>
<Style TargetType="Button">
<Setter Property="Background" Value="#f0f0f0"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Padding" Value="20 10"/>
<Setter Property="Cursor" Value="Hand"/>
<EventSetter Event="Click" Handler="Button_Click"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border
Margin="{TemplateBinding Margin}"
Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}">
<ContentPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="Background.(SolidColorBrush.Color)"
To="#c7c7c7"
Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="Background.(SolidColorBrush.Color)"
To="#f0f0f0"
Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
</StackPanel.Resources>
<Button Content="Edit" Command="{Binding EditCommand}"/>
<Button Content="Delete" Command="{Binding DeleteCommand}"/>
</StackPanel>
</Border>
</custom:DropdownMenu>
</Grid>
</Border>
</UserControl>

View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace YouTubeViewers.WPF.Components
{
/// <summary>
/// Interaction logic for YouTubeViewersListingItem.xaml
/// </summary>
public partial class YouTubeViewersListingItem : UserControl
{
public YouTubeViewersListingItem()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
dropdown.IsOpen = false;
}
}
}

View File

@ -0,0 +1,34 @@
<Window x:Class="YouTubeViewers.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:local="clr-namespace:YouTubeViewers.WPF"
xmlns:vms ="clr-namespace:YouTubeViewers.WPF.ViewModels"
xmlns:views="clr-namespace:YouTubeViewers.WPF.Views"
xmlns:viewmodels="clr-namespace:YouTubeViewers.WPF.ViewModels"
xmlns:custom="clr-namespace:ModalControl;assembly=ModalControl"
mc:Ignorable="d"
Title="YouTube Viewers"
Height="450"
Width="800"
FontSize="14"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<DataTemplate DataType="{x:Type vms:AddYouTubeViewerViewModel}">
<views:AddYouTubeViewerView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vms:EditYouTubeViewerViewModel}">
<views:EditYouTubeViewerView/>
</DataTemplate>
</Window.Resources>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Grid >
<custom:Modal Panel.ZIndex="1" IsOpen="{Binding IsModalOpen}">
<ContentControl Margin="50 25" Content="{Binding CurrentModalViewModel}"/>
</custom:Modal>
<views:YouTubeViewersView Margin="25" DataContext="{Binding YouTubeViewersViewModel}" />
</Grid>
</ScrollViewer>
</Window>

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace YouTubeViewers.WPF
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}

View File

@ -0,0 +1,28 @@
using System;
using YouTubeViewers.WPF.ViewModels;
namespace YouTubeViewers.WPF.Stores
{
public class ModalNavigationStore
{
private ViewModelBase _currentViewModel;
public ViewModelBase CurrentViewModel
{
get { return _currentViewModel; }
set {
_currentViewModel = value;
CurrentViewModelChanged?.Invoke();
}
}
public bool IsOpen => CurrentViewModel != null;
public event Action CurrentViewModelChanged;
public void Close()
{
CurrentViewModel = null;
}
}
}

View File

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YouTubeViewers.Domain.Models;
namespace YouTubeViewers.WPF.Stores
{
public class SelectedYouTubeViewerStore
{
private readonly YouTubeViewersStore _youTubeViewersStore;
public SelectedYouTubeViewerStore(YouTubeViewersStore youTubeViewersStore)
{
_youTubeViewersStore = youTubeViewersStore;
_youTubeViewersStore.YouTubeViewerUpdated += YouTubeViewersStore_YouTubeViewerUpdated;
}
private void YouTubeViewersStore_YouTubeViewerUpdated(YouTubeViewer youTubeViewer)
{
if (youTubeViewer.Id == SelectedYouTubeViewer?.Id)
{
SelectedYouTubeViewer = youTubeViewer;
}
}
private YouTubeViewer _selectedYouTubeViewer;
public YouTubeViewer SelectedYouTubeViewer
{
get { return _selectedYouTubeViewer; }
set
{
_selectedYouTubeViewer = value;
SelectedYouTubeViewerChanged?.Invoke();
}
}
public event Action SelectedYouTubeViewerChanged;
}
}

View File

@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YouTubeViewers.Domain.Commands;
using YouTubeViewers.Domain.Models;
using YouTubeViewers.Domain.Queries;
namespace YouTubeViewers.WPF.Stores
{
public class YouTubeViewersStore
{
private readonly IGetAllYouTubeViewersQuery _getAllYouTubeViewersQuery;
private readonly ICreateYouTubeViewerCommand _createYouTubeViewerCommand;
private readonly IUpdateYouTubeViewerCommand _updateYouTubeViewerCommand;
private readonly IDeleteYouTubeViewerCommand _deleteYouTubeViewerCommand;
private readonly List<YouTubeViewer> _youTubeViewers;
public IEnumerable<YouTubeViewer> YouTubeViewers => _youTubeViewers;
public event Action YouTubeViewersLoaded;
public event Action<YouTubeViewer> YouTubeViewerAdded;
public event Action<YouTubeViewer> YouTubeViewerUpdated;
public event Action<Guid> YouTubeViewerDeleted;
public YouTubeViewersStore(
IGetAllYouTubeViewersQuery getAllYouTubeViewersQuery,
ICreateYouTubeViewerCommand createYouTubeViewerCommand,
IUpdateYouTubeViewerCommand updateYouTubeViewerCommand,
IDeleteYouTubeViewerCommand deleteYouTubeViewerCommand)
{
_getAllYouTubeViewersQuery = getAllYouTubeViewersQuery;
_createYouTubeViewerCommand = createYouTubeViewerCommand;
_updateYouTubeViewerCommand = updateYouTubeViewerCommand;
_deleteYouTubeViewerCommand = deleteYouTubeViewerCommand;
_youTubeViewers = new List<YouTubeViewer>();
}
public async Task Load()
{
IEnumerable<YouTubeViewer> youTubeViewers = await _getAllYouTubeViewersQuery.Execute();
_youTubeViewers.Clear();
_youTubeViewers.AddRange(youTubeViewers);
YouTubeViewersLoaded?.Invoke();
}
public async Task Add(YouTubeViewer youTubeViewer)
{
await _createYouTubeViewerCommand.Execute(youTubeViewer);
_youTubeViewers.Add(youTubeViewer);
YouTubeViewerAdded?.Invoke(youTubeViewer);
}
public async Task Update(YouTubeViewer youTubeViewer)
{
await _updateYouTubeViewerCommand.Execute(youTubeViewer);
int currentIndex = _youTubeViewers.FindIndex(y => y.Id == youTubeViewer.Id);
if(currentIndex != -1)
{
_youTubeViewers[currentIndex] = youTubeViewer;
}
else
{
_youTubeViewers.Add(youTubeViewer);
}
YouTubeViewerUpdated?.Invoke(youTubeViewer);
}
public async Task Delete(Guid id)
{
await _deleteYouTubeViewerCommand.Execute(id);
_youTubeViewers.RemoveAll(y => y.Id == id);
YouTubeViewerDeleted?.Invoke(id);
}
}
}

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using YouTubeViewers.WPF.Commands;
using YouTubeViewers.WPF.Stores;
namespace YouTubeViewers.WPF.ViewModels;
public class AddYouTubeViewerViewModel : ViewModelBase
{
public YouTubeViewerDetailsFormViewModel YouTubeViewerDetailsFormViewModel { get; }
public AddYouTubeViewerViewModel(YouTubeViewersStore _youTubeViewersStore, ModalNavigationStore modalNavigationStore)
{
ICommand cancelCommand = new CloseModalCommand( modalNavigationStore);
ICommand submitCommand = new AddYouTubeViewerCommand(this , _youTubeViewersStore,modalNavigationStore);
YouTubeViewerDetailsFormViewModel = new YouTubeViewerDetailsFormViewModel(submitCommand,cancelCommand);
}
}

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using YouTubeViewers.WPF.Commands;
using YouTubeViewers.Domain.Models;
using YouTubeViewers.WPF.Stores;
namespace YouTubeViewers.WPF.ViewModels;
public class EditYouTubeViewerViewModel : ViewModelBase
{
public Guid YouTubeViewerId { get; }
public YouTubeViewerDetailsFormViewModel YouTubeViewerDetailsFormViewModel { get; }
public EditYouTubeViewerViewModel(YouTubeViewer youTubeViewer, YouTubeViewersStore _youTubeViewersStore, ModalNavigationStore modalNavigationStore)
{
YouTubeViewerId = youTubeViewer.Id;
ICommand submitCommand = new EditYouTubeViewerCommand(this, _youTubeViewersStore, modalNavigationStore);
ICommand cancelCommand = new CloseModalCommand(modalNavigationStore);
YouTubeViewerDetailsFormViewModel = new YouTubeViewerDetailsFormViewModel(submitCommand, cancelCommand)
{
UserName = youTubeViewer.Username,
IsSubScribed = youTubeViewer.IsSubscribed,
IsMember = youTubeViewer.IsMember
};
}
}

View File

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YouTubeViewers.WPF.Stores;
namespace YouTubeViewers.WPF.ViewModels
{
public class MainViewModel : ViewModelBase
{
private readonly ModalNavigationStore _modalNavigationStore;
public ViewModelBase CurrentModalViewModel => _modalNavigationStore.CurrentViewModel;
public bool IsModalOpen => _modalNavigationStore.IsOpen;
public YouTubeViewersViewModel YouTubeViewersViewModel { get; }
public MainViewModel(ModalNavigationStore modalNavigationStore, YouTubeViewersViewModel youTubeViewersViewModel)
{
_modalNavigationStore = modalNavigationStore;
YouTubeViewersViewModel = youTubeViewersViewModel;
_modalNavigationStore.CurrentViewModelChanged += ModalNavigationStore_CurrenyViewModelChanged;
}
protected override void Dispose()
{
_modalNavigationStore.CurrentViewModelChanged -= ModalNavigationStore_CurrenyViewModelChanged;
base.Dispose();
}
private void ModalNavigationStore_CurrenyViewModelChanged()
{
OnPropertyChanged(nameof(CurrentModalViewModel));
OnPropertyChanged(nameof(IsModalOpen));
}
}
}

View File

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace YouTubeViewers.WPF.ViewModels
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void Dispose() { }
}
}

View File

@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace YouTubeViewers.WPF.ViewModels;
public class YouTubeViewerDetailsFormViewModel : ViewModelBase
{
private string _userName;
public string UserName
{
get { return _userName; }
set
{
_userName = value;
OnPropertyChanged(nameof(UserName));
OnPropertyChanged(nameof(CanSubmit));
}
}
private bool _isSubScribed;
public bool IsSubScribed
{
get { return _isSubScribed; }
set
{
_isSubScribed = value;
OnPropertyChanged(nameof(IsSubScribed));
}
}
private bool _isMember;
public bool IsMember
{
get { return _isMember; }
set
{
_isMember = value;
OnPropertyChanged(nameof(IsMember));
}
}
#region IsSubmitting
private bool _IsSubmitting;
public bool IsSubmitting
{
get { return _IsSubmitting; }
set { _IsSubmitting = value; OnPropertyChanged(nameof(IsSubmitting)); }
}
#endregion IsSubmitting
public bool CanSubmit => !string.IsNullOrEmpty(UserName);
public ICommand SubmitCommand { get; }
public ICommand CancelCommand { get; }
public YouTubeViewerDetailsFormViewModel(ICommand submitCommand, ICommand cancelCommand)
{
SubmitCommand = submitCommand;
CancelCommand = cancelCommand;
}
}

View File

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YouTubeViewers.WPF.Stores;
using YouTubeViewers.Domain.Models;
namespace YouTubeViewers.WPF.ViewModels
{
public class YouTubeViewersDetailsViewModel : ViewModelBase
{
private readonly SelectedYouTubeViewerStore _selectedYouTubeViewerStore;
private YouTubeViewer SelectedYouTubeViewer => _selectedYouTubeViewerStore.SelectedYouTubeViewer;
public bool HasSelectedYouTubeViewer => SelectedYouTubeViewer != null;
public string UserName => SelectedYouTubeViewer?.Username ?? "Unknown";
public string IsSubScribedDisplay => (SelectedYouTubeViewer?.IsSubscribed ?? false) ? "Yes" : "No";
public string IsMemberDisplay => (SelectedYouTubeViewer?.IsMember ?? false) ? "Yes" : "No";
public YouTubeViewersDetailsViewModel(SelectedYouTubeViewerStore selectedYouTubeViewerStore)
{
_selectedYouTubeViewerStore = selectedYouTubeViewerStore;
_selectedYouTubeViewerStore.SelectedYouTubeViewerChanged += SelectedYouTubeViewerStore_SelectedYouTubeViewerChanged;
}
protected override void Dispose()
{
_selectedYouTubeViewerStore.SelectedYouTubeViewerChanged -= SelectedYouTubeViewerStore_SelectedYouTubeViewerChanged;
base.Dispose();
}
private void SelectedYouTubeViewerStore_SelectedYouTubeViewerChanged()
{
OnPropertyChanged(nameof(HasSelectedYouTubeViewer));
OnPropertyChanged(nameof(UserName));
OnPropertyChanged(nameof(IsSubScribedDisplay));
OnPropertyChanged(nameof(IsMemberDisplay));
}
}
}

View File

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using YouTubeViewers.WPF.Commands;
using YouTubeViewers.Domain.Models;
using YouTubeViewers.WPF.Stores;
namespace YouTubeViewers.WPF.ViewModels
{
public class YouTubeViewersListingItemViewModel : ViewModelBase
{
//private YouTubeViewer _youTubeViewer;
//private YouTubeViewersStore _youTubeViewersStore;
//private ModalNavigationStore _modalNavigationStore;
public YouTubeViewer YouTubeViewer { get; private set; }
public string UserName => YouTubeViewer.Username;
public ICommand EditCommand { get; }
public ICommand DeleteCommand { get; }
public YouTubeViewersListingItemViewModel(YouTubeViewer youTubeViewer, YouTubeViewersStore youTubeViewersStore, ModalNavigationStore modalNavigationStore)
{
YouTubeViewer = youTubeViewer;
EditCommand = new OpenEditYouTubeViewerCommand(this, youTubeViewersStore, modalNavigationStore);
DeleteCommand = new DeleteYouTubeViewerCommand(this, youTubeViewersStore);
}
public void Update(YouTubeViewer youTubeViewer)
{
YouTubeViewer = youTubeViewer;
OnPropertyChanged(nameof(UserName));
}
}
}

View File

@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using YouTubeViewers.WPF.Commands;
using YouTubeViewers.Domain.Models;
using YouTubeViewers.WPF.Stores;
namespace YouTubeViewers.WPF.ViewModels
{
public class YouTubeViewersListingViewModel : ViewModelBase
{
private readonly ObservableCollection<YouTubeViewersListingItemViewModel> _youtubeViewersListingItemViewModels;
private readonly YouTubeViewersStore _youTubeViewersStore;
private readonly SelectedYouTubeViewerStore _selectedYouTubeViewerStore;
private readonly ModalNavigationStore _modalNavigationStore;
public IEnumerable<YouTubeViewersListingItemViewModel> YouTubeViewersListingItemViewModels => _youtubeViewersListingItemViewModels;
private YouTubeViewersListingItemViewModel _selectedYouTubeViewerListingItemViewModel;
public YouTubeViewersListingItemViewModel SelectedYouTubeViewerListingItemViewModel
{
get { return _selectedYouTubeViewerListingItemViewModel; }
set
{
_selectedYouTubeViewerListingItemViewModel = value;
OnPropertyChanged(nameof(SelectedYouTubeViewerListingItemViewModel));
_selectedYouTubeViewerStore.SelectedYouTubeViewer = _selectedYouTubeViewerListingItemViewModel?.YouTubeViewer;
}
}
public YouTubeViewersListingViewModel(
YouTubeViewersStore youTubeViewersStore,
SelectedYouTubeViewerStore selectedYouTubeViewerStore,
ModalNavigationStore modalNavigationStore)
{
_youTubeViewersStore = youTubeViewersStore;
_selectedYouTubeViewerStore = selectedYouTubeViewerStore;
_modalNavigationStore = modalNavigationStore;
_youtubeViewersListingItemViewModels = new ObservableCollection<YouTubeViewersListingItemViewModel>();
_youTubeViewersStore.YouTubeViewersLoaded += _youTubeViewersStore_YouTubeViewersLoaded;
_youTubeViewersStore.YouTubeViewerAdded += YouTubeViewersStore_YouTubeViewerAdded;
_youTubeViewersStore.YouTubeViewerUpdated += YouTubeViewersStore_YouTubeViewerUpdated;
_youTubeViewersStore.YouTubeViewerDeleted += YouTubeViewersStore_YouTubeViewerDeleted;
}
private void _youTubeViewersStore_YouTubeViewersLoaded()
{
_youtubeViewersListingItemViewModels.Clear();
foreach (YouTubeViewer youTubeViewer in _youTubeViewersStore.YouTubeViewers)
{
AddYouTubeViewer(youTubeViewer);
}
}
private void YouTubeViewersStore_YouTubeViewerUpdated(YouTubeViewer youTubeViewer)
{
YouTubeViewersListingItemViewModel youTubeViewerViewModel =
_youtubeViewersListingItemViewModels.FirstOrDefault(x => x.YouTubeViewer.Id == youTubeViewer.Id);
if (youTubeViewerViewModel != null)
{
youTubeViewerViewModel.Update(youTubeViewer);
}
}
protected override void Dispose()
{
_youTubeViewersStore.YouTubeViewersLoaded -= _youTubeViewersStore_YouTubeViewersLoaded;
_youTubeViewersStore.YouTubeViewerAdded -= YouTubeViewersStore_YouTubeViewerAdded;
_youTubeViewersStore.YouTubeViewerUpdated -= YouTubeViewersStore_YouTubeViewerUpdated;
_youTubeViewersStore.YouTubeViewerDeleted -= YouTubeViewersStore_YouTubeViewerDeleted;
base.Dispose();
}
private void YouTubeViewersStore_YouTubeViewerAdded(YouTubeViewer youTubeViewer)
{
AddYouTubeViewer(youTubeViewer);
}
private void AddYouTubeViewer(YouTubeViewer youTubeViewer)
{
YouTubeViewersListingItemViewModel itemViewModel =
new YouTubeViewersListingItemViewModel(youTubeViewer, _youTubeViewersStore, _modalNavigationStore);
_youtubeViewersListingItemViewModels.Add(itemViewModel);
}
private void YouTubeViewersStore_YouTubeViewerDeleted(Guid id)
{
YouTubeViewersListingItemViewModel itemViewModel = _youtubeViewersListingItemViewModels.FirstOrDefault(y => y.YouTubeViewer?.Id == id);
if (itemViewModel != null)
{
_youtubeViewersListingItemViewModels.Remove(itemViewModel);
}
}
}
}

View File

@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using YouTubeViewers.WPF.Commands;
using YouTubeViewers.WPF.Stores;
namespace YouTubeViewers.WPF.ViewModels
{
public class YouTubeViewersViewModel : ViewModelBase
{
public YouTubeViewersListingViewModel YouTubeViewersListingViewModel { get; }
public YouTubeViewersDetailsViewModel YouTubeViewersDetailsViewModel { get; }
private bool _IsLoading;
public bool IsLoading
{
get
{
return _IsLoading;
}
set
{
_IsLoading = value;
OnPropertyChanged(nameof(IsLoading));
}
}
public ICommand LoadYouTubeViewersCommand { get; }
public ICommand AddYouTubeViewersCommand { get; }
public YouTubeViewersViewModel(YouTubeViewersStore youTubeViewersStore, SelectedYouTubeViewerStore _selectedYouTubeViewerStore, ModalNavigationStore modalNavigationStore)
{
YouTubeViewersListingViewModel = new YouTubeViewersListingViewModel(youTubeViewersStore, _selectedYouTubeViewerStore, modalNavigationStore);
YouTubeViewersDetailsViewModel = new YouTubeViewersDetailsViewModel(_selectedYouTubeViewerStore);
LoadYouTubeViewersCommand = new LoadYouTubeViewersCommand(this,youTubeViewersStore);
AddYouTubeViewersCommand = new OpenAddYouTubeViewerCommand(youTubeViewersStore, modalNavigationStore);
}
public static YouTubeViewersViewModel LoadViewModel(
YouTubeViewersStore youTubeViewersStore,
SelectedYouTubeViewerStore selectedYouTubeViewerStore,
ModalNavigationStore modalNavigationStore)
{
YouTubeViewersViewModel viewModel = new YouTubeViewersViewModel(youTubeViewersStore, selectedYouTubeViewerStore, modalNavigationStore);
viewModel.LoadYouTubeViewersCommand.Execute(null);
return viewModel;
}
}
}

View File

@ -0,0 +1,26 @@
<UserControl x:Class="YouTubeViewers.WPF.Views.AddYouTubeViewerView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:YouTubeViewers.WPF.Views"
xmlns:components="clr-namespace:YouTubeViewers.WPF.Components"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0"
Style="{StaticResource PageHeader}"
Text="Add YouTube Viewers" />
<components:YouTubeViewerDetailsForm Grid.Row="1" Margin="0 30 0 0 " DataContext="{Binding YouTubeViewerDetailsFormViewModel}"/>
</Grid>
</UserControl>

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace YouTubeViewers.WPF.Views
{
/// <summary>
/// Interaction logic for AddYouTubeViewerView.xaml
/// </summary>
public partial class AddYouTubeViewerView : UserControl
{
public AddYouTubeViewerView()
{
InitializeComponent();
}
}
}

View File

@ -0,0 +1,24 @@
<UserControl x:Class="YouTubeViewers.WPF.Views.EditYouTubeViewerView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:YouTubeViewers.WPF.Views"
xmlns:components="clr-namespace:YouTubeViewers.WPF.Components"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0"
Style="{StaticResource PageHeader}"
Text="Edit YouTube Viewers" />
<components:YouTubeViewerDetailsForm Grid.Row="1" Margin="0 30 0 0 " DataContext="{Binding YouTubeViewerDetailsFormViewModel}"/>
</Grid>
</UserControl>

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace YouTubeViewers.WPF.Views
{
/// <summary>
/// Interaction logic for EditYouTubeViewerView.xaml
/// </summary>
public partial class EditYouTubeViewerView : UserControl
{
public EditYouTubeViewerView()
{
InitializeComponent();
}
}
}

View File

@ -0,0 +1,59 @@
<UserControl x:Class="YouTubeViewers.WPF.Views.YouTubeViewersView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:YouTubeViewers.WPF.Views"
xmlns:components="clr-namespace:YouTubeViewers.WPF.Components"
xmlns:custom="clr-namespace:LoadingSpinnerControl;assembly=LoadingSpinnerControl"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<StackPanel VerticalAlignment="Center">
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsLoading}" Value="true">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<custom:LoadingSpinner Diameter="100" IsLoading="True" Thickness="3"/>
<TextBlock Margin="0 10 0 0" Text="Loading YouTube Viewers..." TextAlignment="Center" />
</StackPanel>
<Grid>
<Grid.Style>
<Style TargetType="Grid">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsLoading}" Value="false">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Style="{StaticResource PageHeader}" Text="YouTube Viewers" />
<Button Grid.Column="1" Content="Add" FontSize="16" Command="{Binding AddYouTubeViewersCommand}"/>
</Grid>
<Grid Grid.Row="1" Margin="0 20 0 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<components:YouTubeViewersListing Grid.Column="0" DataContext="{Binding YouTubeViewersListingViewModel}"/>
<components:YouTubeViewersDetails Grid.Column="1" Margin="20 0 0 0" DataContext="{Binding YouTubeViewersDetailsViewModel}"/>
</Grid>
</Grid>
</Grid>
</UserControl>

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace YouTubeViewers.WPF.Views
{
/// <summary>
/// Interaction logic for YouTubeViewersView.xaml
/// </summary>
public partial class YouTubeViewersView : UserControl
{
public YouTubeViewersView()
{
InitializeComponent();
}
}
}

View File

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DropdownMenu.WPF" Version="1.0.0" />
<PackageReference Include="LoadingSpinner.WPF" Version="1.0.0" />
<PackageReference Include="SimpleModal.WPF" Version="1.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\YouTubeViewers.Domain\YouTubeViewers.Domain.csproj" />
<ProjectReference Include="..\YouTubeViewers.EntityFramework\YouTubeViewers.EntityFramework.csproj" />
</ItemGroup>
</Project>