Made wpf sample more mvvm with multi page support.

This commit is contained in:
soukoku
2014-11-15 13:13:41 -05:00
parent 6370d9c38b
commit df80b86f71
6 changed files with 293 additions and 107 deletions

View File

@@ -2,8 +2,17 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:modern="http://modernwpf" xmlns:modern="http://modernwpf"
Title="TWAIN Data Source Tester" Height="600" Width="900" ResizeMode="CanResizeWithGrip" xmlns:proj="clr-namespace:Tester.WPF"
Title="{Binding AppTitle}"
Height="600" Width="900" ResizeMode="CanResizeWithGrip"
x:Name="theWindow"
Style="{StaticResource AppWindow}"> Style="{StaticResource AppWindow}">
<Window.Resources>
<proj:TwainVM x:Key="vm"></proj:TwainVM>
</Window.Resources>
<Window.DataContext>
<StaticResource ResourceKey="vm"></StaticResource>
</Window.DataContext>
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="auto" /> <RowDefinition Height="auto" />
@@ -19,7 +28,8 @@
<Label Content="Sources"></Label> <Label Content="Sources"></Label>
<ListBox x:Name="SrcList" Grid.Row="1" Grid.RowSpan="2" Width="150" <ListBox x:Name="SrcList" Grid.Row="1" Grid.RowSpan="2" Width="150"
SelectionChanged="SrcList_SelectionChanged" ItemsSource="{Binding DataSources}"
SelectedItem="{Binding SelectedSource}"
Style="{StaticResource AppListBox}"> Style="{StaticResource AppListBox}">
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
<DataTemplate> <DataTemplate>
@@ -35,11 +45,11 @@
</ListBox> </ListBox>
<Label Content="Caps" Grid.Column="1"></Label> <Label Content="Caps" Grid.Column="1"></Label>
<TextBox Text="{Binding CapFilter, UpdateSourceTrigger=PropertyChanged}" DataContext="{Binding ElementName=SrcList, Path=SelectedItem}" <TextBox Text="{Binding CapFilter, UpdateSourceTrigger=PropertyChanged}" DataContext="{Binding SelectedSource}"
Grid.Column="1" Grid.Row="1" Grid.Column="1" Grid.Row="1"
modern:TextBoxUI.WatermarkText="Find cap name"></TextBox> modern:TextBoxUI.WatermarkText="Find cap name"></TextBox>
<ListBox x:Name="CapList" Grid.Row="2" Grid.Column="1" Width="150" <ListBox x:Name="CapList" Grid.Row="2" Grid.Column="1" Width="150"
DataContext="{Binding ElementName=SrcList, Path=SelectedItem}" DataContext="{Binding SelectedSource}"
ItemsSource="{Binding Caps}" ItemsSource="{Binding Caps}"
SelectionChanged="CapList_SelectionChanged" SelectionChanged="CapList_SelectionChanged"
Style="{StaticResource AppListBox}"> Style="{StaticResource AppListBox}">
@@ -51,7 +61,8 @@
FontStyle="Italic"/> FontStyle="Italic"/>
</StackPanel> </StackPanel>
</DataTemplate> </DataTemplate>
</ListBox.ItemTemplate></ListBox> </ListBox.ItemTemplate>
</ListBox>
<Label Content="Cap values" Grid.Column="2"></Label> <Label Content="Cap values" Grid.Column="2"></Label>
<ListBox x:Name="CapDetailList" Grid.Row="1" Grid.RowSpan="2" Grid.Column="2" MinWidth="100" <ListBox x:Name="CapDetailList" Grid.Row="1" Grid.RowSpan="2" Grid.Column="2" MinWidth="100"
@@ -68,13 +79,34 @@
<StackPanel Orientation="Horizontal" Grid.Column="3" > <StackPanel Orientation="Horizontal" Grid.Column="3" >
<Label Content="State:"></Label> <Label Content="State:"></Label>
<TextBlock Text="{Binding State}"></TextBlock> <TextBlock Text="{Binding State}"></TextBlock>
<Button Content="Test acquire" Click="Button_Click_1" Margin="4 0"></Button> <Button Content="_Driver settings" Command="{Binding ShowDriverCommand}"></Button>
<Button Content="_Start capture" Command="{Binding CaptureCommand}"></Button>
<Slider Maximum="{Binding MaxThumbnailSize}" Minimum="{Binding MinThumbnailSize}"
Value="{Binding ThumbnailSize}" Width="100" LargeChange="20" SmallChange="10"
VerticalAlignment="Center"
ToolTip="{Binding ThumbnailSize}"></Slider>
<TextBlock Text="{Binding CapturedImages.Count, StringFormat='{}{0} pages'}"></TextBlock>
<Button Content="_Clear pages" Command="{Binding ClearCommand}"></Button>
</StackPanel> </StackPanel>
<modern:AnimatedScrollViewer Grid.Row="1" Grid.RowSpan="2" Grid.Column="3" VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto"> <ListBox Grid.Row="1" Grid.RowSpan="2" Grid.Column="3"
<Image Stretch="Uniform" MaxWidth="1000" ItemsSource="{Binding CapturedImages}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel ScrollViewer.HorizontalScrollBarVisibility="Disabled"></WrapPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Border Padding="4">
<Image Stretch="Uniform"
RenderOptions.BitmapScalingMode="HighQuality" RenderOptions.BitmapScalingMode="HighQuality"
Source="{Binding Image}"></Image> Width="{Binding ElementName=theWindow, Path=DataContext.ThumbnailSize}"
</modern:AnimatedScrollViewer> Height="{Binding ElementName=theWindow, Path=DataContext.ThumbnailSize}"
Source="{Binding}"></Image>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid> </Grid>
</Window> </Window>

View File

@@ -1,5 +1,6 @@
using GalaSoft.MvvmLight.Messaging; using GalaSoft.MvvmLight.Messaging;
using ModernWPF.Controls; using ModernWPF.Controls;
using ModernWPF.Messages;
using NTwain; using NTwain;
using NTwain.Data; using NTwain.Data;
using System; using System;
@@ -26,29 +27,20 @@ namespace Tester.WPF
InitializeComponent(); InitializeComponent();
if (!DesignerProperties.GetIsInDesignMode(this)) if (!DesignerProperties.GetIsInDesignMode(this))
{ {
if (PlatformInfo.Current.IsApp64Bit) _twainVM = this.DataContext as TwainVM;
{
Title = Title + " (64bit)";
}
else
{
Title = Title + " (32bit)";
}
_twainVM = new TwainVM();
this.DataContext = _twainVM;
Messenger.Default.Register<RefreshCommandsMessage>(this, m => m.HandleRefreshCommands());
Messenger.Default.Register<DialogMessage>(this, msg => Messenger.Default.Register<DialogMessage>(this, msg =>
{ {
if (Dispatcher.CheckAccess()) if (Dispatcher.CheckAccess())
{ {
ModernMessageBox.Show(this, msg.Content, msg.Caption, msg.Button, msg.Icon, msg.DefaultResult); this.HandleDialogMessageModern(msg);
} }
else else
{ {
Dispatcher.BeginInvoke(new Action(() => Dispatcher.BeginInvoke(new Action(() =>
{ {
ModernMessageBox.Show(this, msg.Content, msg.Caption, msg.Button, msg.Icon, msg.DefaultResult); this.HandleDialogMessageModern(msg);
})); }));
} }
}); });
@@ -61,48 +53,16 @@ namespace Tester.WPF
} }
protected override void OnClosed(EventArgs e) protected override void OnClosed(EventArgs e)
{ {
if (_twainVM.State == 4) _twainVM.CloseDown();
{
_twainVM.CurrentSource.Close();
}
_twainVM.Close();
base.OnClosed(e); base.OnClosed(e);
} }
protected override void OnSourceInitialized(EventArgs e) protected override void OnSourceInitialized(EventArgs e)
{ {
base.OnSourceInitialized(e); base.OnSourceInitialized(e);
_twainVM.WindowHandle = new WindowInteropHelper(this).Handle;
// use this for internal msg loop
//var rc = _twainVM.Open();
// use this to hook into current app loop
var rc = _twainVM.Open(new WpfMessageLoopHook(new WindowInteropHelper(this).Handle));
if (rc == ReturnCode.Success)
{
SrcList.ItemsSource = _twainVM.Select(s => new DSVM { DS = s });
} }
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
_twainVM.TestCapture(new WindowInteropHelper(this).Handle);
}
private void SrcList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (_twainVM.State == 4)
{
_twainVM.CurrentSource.Close();
}
var dsId = SrcList.SelectedItem as DSVM;
if (dsId != null)
{
dsId.Open();
}
}
private void CapList_SelectionChanged(object sender, SelectionChangedEventArgs e) private void CapList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{ {

View File

@@ -59,10 +59,22 @@
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\..\packages\CommonServiceLocator.1.3\lib\portable-net4+sl5+netcore45+wpa81+wp8\Microsoft.Practices.ServiceLocation.dll</HintPath> <HintPath>..\..\packages\CommonServiceLocator.1.3\lib\portable-net4+sl5+netcore45+wpa81+wp8\Microsoft.Practices.ServiceLocation.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.WindowsAPICodePack">
<HintPath>..\..\packages\Microsoft.WindowsAPICodePack-Core.1.1.0.0\lib\Microsoft.WindowsAPICodePack.dll</HintPath>
</Reference>
<Reference Include="Microsoft.WindowsAPICodePack.Shell">
<HintPath>..\..\packages\Microsoft.WindowsAPICodePack-Shell.1.1.0.0\lib\Microsoft.WindowsAPICodePack.Shell.dll</HintPath>
</Reference>
<Reference Include="Microsoft.WindowsAPICodePack.ShellExtensions">
<HintPath>..\..\packages\Microsoft.WindowsAPICodePack-Shell.1.1.0.0\lib\Microsoft.WindowsAPICodePack.ShellExtensions.dll</HintPath>
</Reference>
<Reference Include="ModernWPF, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c99d0cfbea7491ef, processorArchitecture=MSIL"> <Reference Include="ModernWPF, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c99d0cfbea7491ef, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\..\packages\ModernWPF.1.2.9.1\lib\net40-Client\ModernWPF.dll</HintPath> <HintPath>..\..\packages\ModernWPF.1.2.9.1\lib\net40-Client\ModernWPF.dll</HintPath>
</Reference> </Reference>
<Reference Include="ModernWPF.Mvvm">
<HintPath>..\..\packages\ModernWPF.Mvvm.0.7.0\lib\net40-Client\ModernWPF.Mvvm.dll</HintPath>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Windows.Forms" /> <Reference Include="System.Windows.Forms" />
@@ -103,7 +115,7 @@
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="ViewModels\CapVM.cs" /> <Compile Include="ViewModels\CapVM.cs" />
<Compile Include="ViewModels\DSVM.cs" /> <Compile Include="ViewModels\DataSourceVM.cs" />
<Compile Include="MainWindow.xaml.cs"> <Compile Include="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon> <DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType> <SubType>Code</SubType>

View File

@@ -12,7 +12,7 @@ namespace Tester.WPF
/// <summary> /// <summary>
/// Wraps a data source as view model. /// Wraps a data source as view model.
/// </summary> /// </summary>
class DSVM : ViewModelBase class DataSourceVM : ViewModelBase
{ {
public DataSource DS { get; set; } public DataSource DS { get; set; }
@@ -21,7 +21,7 @@ namespace Tester.WPF
public string Protocol { get { return DS.ProtocolVersion.ToString(); } } public string Protocol { get { return DS.ProtocolVersion.ToString(); } }
ICollectionView _capView; ICollectionView _capView;
public DSVM() public DataSourceVM()
{ {
Caps = new ObservableCollection<CapVM>(); Caps = new ObservableCollection<CapVM>();
_capView = CollectionViewSource.GetDefaultView(Caps); _capView = CollectionViewSource.GetDefaultView(Caps);

View File

@@ -9,39 +9,216 @@ using System.Reflection;
using System.Threading; using System.Threading;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using System.Collections.ObjectModel;
using GalaSoft.MvvmLight;
using System.Windows.Input;
using GalaSoft.MvvmLight.Command;
using ModernWPF;
using ModernWPF.Messages;
namespace Tester.WPF namespace Tester.WPF
{ {
/// <summary> /// <summary>
/// Wraps the twain session as a view model for databinding. /// Wraps the twain session as a view model for databinding.
/// </summary> /// </summary>
class TwainVM : TwainSession class TwainVM : ViewModelBase
{ {
public TwainVM() public TwainVM()
: base(TWIdentity.CreateFromAssembly(DataGroups.Image | DataGroups.Audio, Assembly.GetEntryAssembly()))
{ {
DataSources = new ObservableCollection<DataSourceVM>();
CapturedImages = new ObservableCollection<ImageSource>();
//this.SynchronizationContext = SynchronizationContext.Current; //this.SynchronizationContext = SynchronizationContext.Current;
var appId = TWIdentity.CreateFromAssembly(DataGroups.Image | DataGroups.Audio, Assembly.GetEntryAssembly());
_session = new TwainSession(appId);
_session.TransferError += _session_TransferError;
_session.TransferReady += _session_TransferReady;
_session.DataTransferred += _session_DataTransferred;
_session.SourceDisabled += _session_SourceDisabled;
} }
private ImageSource _image; TwainSession _session;
/// <summary> #region properties
/// Gets or sets the captured image.
/// </summary> public string AppTitle
/// <value>
/// The image.
/// </value>
public ImageSource Image
{ {
get { return _image; } get
{
if (NTwain.PlatformInfo.Current.IsApp64Bit)
{
return "TWAIN Data Source Tester (64bit)";
}
else
{
return "TWAIN Data Source Tester (32bit)";
}
}
}
public ObservableCollection<DataSourceVM> DataSources { get; private set; }
private DataSourceVM _selectedSource;
public DataSourceVM SelectedSource
{
get { return _selectedSource; }
set set
{ {
_image = value; if (_session.State == 4)
OnPropertyChanged("Image"); {
_session.CurrentSource.Close();
}
_selectedSource = value;
RaisePropertyChanged(() => SelectedSource);
if (_selectedSource != null)
{
_selectedSource.Open();
}
} }
} }
protected override void OnTransferError(TransferErrorEventArgs e) public int State { get { return _session.State; } }
private IntPtr _winHandle;
public IntPtr WindowHandle
{
get { return _winHandle; }
set
{
_winHandle = value;
if (value == IntPtr.Zero)
{
}
else
{
// use this for internal msg loop
var rc = _session.Open();
// use this to hook into current app loop
//var rc = _session.Open(new WpfMessageLoopHook(value));
if (rc == ReturnCode.Success)
{
ReloadSourcesCommand.Execute(null);
}
}
}
}
private ICommand _showDriverCommand;
public ICommand ShowDriverCommand
{
get
{
return _showDriverCommand ?? (_showDriverCommand = new RelayCommand(() =>
{
if (_session.State == 4)
{
var rc = _session.CurrentSource.Enable(SourceEnableMode.ShowUIOnly, false, WindowHandle);
}
}, () =>
{
return _session.State == 4 && _session.CurrentSource.CapEnableDSUIOnly.GetCurrent() == BoolType.True;
}));
}
}
private ICommand _captureCommand;
public ICommand CaptureCommand
{
get
{
return _captureCommand ?? (_captureCommand = new RelayCommand(() =>
{
if (_session.State == 4)
{
//if (this.CurrentSource.ICapPixelType.Get().Contains(PixelType.BlackWhite))
//{
// this.CurrentSource.ICapPixelType.Set(PixelType.BlackWhite);
//}
//if (this.CurrentSource.ICapXferMech.Get().Contains(XferMech.File))
//{
// this.CurrentSource.ICapXferMech.Set(XferMech.File);
//}
var rc = _session.CurrentSource.Enable(SourceEnableMode.NoUI, false, WindowHandle);
}
}, () =>
{
return _session.State == 4;
}));
}
}
private ICommand _clearCommand;
public ICommand ClearCommand
{
get
{
return _clearCommand ?? (_clearCommand = new RelayCommand(() =>
{
CapturedImages.Clear();
}, () =>
{
return CapturedImages.Count > 0;
}));
}
}
private ICommand _reloadSrc;
public ICommand ReloadSourcesCommand
{
get
{
return _reloadSrc ?? (_reloadSrc = new RelayCommand(() =>
{
DataSources.Clear();
foreach (var s in _session.Select(s => new DataSourceVM { DS = s }))
{
DataSources.Add(s);
}
}, () =>
{
return _session.State > 2;
}));
}
}
/// <summary>
/// Gets the captured images.
/// </summary>
/// <value>
/// The captured images.
/// </value>
public ObservableCollection<ImageSource> CapturedImages { get; private set; }
public double MinThumbnailSize { get { return 50; } }
public double MaxThumbnailSize { get { return 300; } }
private double _thumbSize = 150;
public double ThumbnailSize
{
get { return _thumbSize; }
set
{
if (value > MaxThumbnailSize) { value = MaxThumbnailSize; }
else if (value < MinThumbnailSize) { value = MinThumbnailSize; }
_thumbSize = value;
RaisePropertyChanged(() => ThumbnailSize);
}
}
#endregion
void _session_SourceDisabled(object sender, EventArgs e)
{
Messenger.Default.Send(new RefreshCommandsMessage());
}
void _session_TransferError(object sender, TransferErrorEventArgs e)
{ {
App.Current.Dispatcher.BeginInvoke(new Action(() => App.Current.Dispatcher.BeginInvoke(new Action(() =>
{ {
@@ -66,25 +243,23 @@ namespace Tester.WPF
})); }));
} }
protected override void OnTransferReady(TransferReadyEventArgs e) void _session_TransferReady(object sender, TransferReadyEventArgs e)
{ {
// set it up to use file xfer if (_session.CurrentSource.ICapXferMech.GetCurrent() == XferMech.File)
if (this.CurrentSource.CapGetCurrent(CapabilityId.ICapXferMech).ConvertToEnum<XferMech>() == XferMech.File)
{ {
var formats = this.CurrentSource.ICapImageFileFormat.Get(); var formats = _session.CurrentSource.ICapImageFileFormat.Get();
var wantFormat = formats.Contains(FileFormat.Tiff) ? FileFormat.Tiff : FileFormat.Bmp; var wantFormat = formats.Contains(FileFormat.Tiff) ? FileFormat.Tiff : FileFormat.Bmp;
var fileSetup = new TWSetupFileXfer var fileSetup = new TWSetupFileXfer
{ {
Format = wantFormat, Format = wantFormat,
FileName = GetUniqueName(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "test", ".tif") FileName = GetUniqueName(Path.GetTempPath(), "twain-test", "." + wantFormat)
}; };
var rc = this.CurrentSource.DGControl.SetupFileXfer.Set(fileSetup); var rc = _session.CurrentSource.DGControl.SetupFileXfer.Set(fileSetup);
} }
} }
private string GetUniqueName(string dir, string name, string ext) string GetUniqueName(string dir, string name, string ext)
{ {
var filePath = Path.Combine(dir, name + ext); var filePath = Path.Combine(dir, name + ext);
int next = 1; int next = 1;
@@ -95,9 +270,22 @@ namespace Tester.WPF
return filePath; return filePath;
} }
protected override void OnDataTransferred(DataTransferredEventArgs e) void _session_DataTransferred(object sender, DataTransferredEventArgs e)
{ {
ImageSource img = null; ImageSource img = GenerateThumbnail(e);
if (img != null)
{
App.Current.Dispatcher.BeginInvoke(new Action(() =>
{
CapturedImages.Add(img);
}));
}
}
ImageSource GenerateThumbnail(DataTransferredEventArgs e)
{
BitmapSource img = null;
if (e.NativeData != IntPtr.Zero) if (e.NativeData != IntPtr.Zero)
{ {
img = e.NativeData.GetWPFBitmap(); img = e.NativeData.GetWPFBitmap();
@@ -106,35 +294,26 @@ namespace Tester.WPF
{ {
img = new BitmapImage(new Uri(e.FileDataPath)); img = new BitmapImage(new Uri(e.FileDataPath));
} }
if (img != null) if (img != null)
{ {
if (img.CanFreeze) // from http://stackoverflow.com/questions/18189501/create-thumbnail-image-directly-from-header-less-image-byte-array
{ var scale = MaxThumbnailSize / img.PixelWidth;
var transform = new ScaleTransform(scale, scale);
var thumbnail = new TransformedBitmap(img, transform);
img = new WriteableBitmap(new TransformedBitmap(img, transform));
img.Freeze(); img.Freeze();
} }
App.Current.Dispatcher.BeginInvoke(new Action(() => return img;
{
Image = img;
}));
}
} }
public void TestCapture(IntPtr hwnd) internal void CloseDown()
{ {
if (State == 4) if (_session.State == 4)
{ {
//if (this.CurrentSource.ICapPixelType.Get().Contains(PixelType.BlackWhite)) _session.CurrentSource.Close();
//{
// this.CurrentSource.ICapPixelType.Set(PixelType.BlackWhite);
//}
//if (this.CurrentSource.ICapXferMech.Get().Contains(XferMech.File))
//{
// this.CurrentSource.ICapXferMech.Set(XferMech.File);
//}
var rc = this.CurrentSource.Enable(SourceEnableMode.NoUI, false, hwnd);
} }
_session.Close();
} }
} }
} }

View File

@@ -2,6 +2,9 @@
<packages> <packages>
<package id="CommonServiceLocator" version="1.3" targetFramework="net40-Client" /> <package id="CommonServiceLocator" version="1.3" targetFramework="net40-Client" />
<package id="CommonWin32" version="2.0.5.5" targetFramework="net40-Client" /> <package id="CommonWin32" version="2.0.5.5" targetFramework="net40-Client" />
<package id="Microsoft.WindowsAPICodePack-Core" version="1.1.0.0" targetFramework="net40-Client" />
<package id="Microsoft.WindowsAPICodePack-Shell" version="1.1.0.0" targetFramework="net40-Client" />
<package id="ModernWPF" version="1.2.9.1" targetFramework="net40-Client" /> <package id="ModernWPF" version="1.2.9.1" targetFramework="net40-Client" />
<package id="ModernWPF.Mvvm" version="0.7.0" targetFramework="net40-Client" />
<package id="MvvmLightLibs" version="5.0.2.0" targetFramework="net40-Client" /> <package id="MvvmLightLibs" version="5.0.2.0" targetFramework="net40-Client" />
</packages> </packages>