Третья и последняя статья про просмотр документов MS Word и Excel из WPF-приложения.
Сделаем тестовое приложение, в котором можно будет походить по папкам и пооткрывать документы.
XAML окна
<Window x:Class="OfficeControlsTestApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewmodels="clr-namespace:OfficeControlsTestApp.ViewModels"
xmlns:controls="clr-namespace:OfficeControlsTestApp.Controls"
xmlns:excel="clr-namespace:WPFExcelControl;assembly=WPFExcelControl"
xmlns:word="clr-namespace:WPFWordControl;assembly=WPFWordControl"
Title="Просмотр документов Word Excel"
Icon="/OfficeControlsTestApp;component/Images/viewer.png"
Height="400" Width="800">
<Window.DataContext>
<viewmodels:MainWindowViewModel />
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300px" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<controls:TreeViewFolderBrowser Grid.Column="0" File="{Binding BrowserFile, Mode=OneWayToSource}" />
<GridSplitter Grid.Column="1"
Width="5"
Background="LightBlue"
VerticalAlignment="Stretch" HorizontalAlignment="Center" />
<Canvas Grid.Column="2" x:Name="canvas">
<excel:Excel File="{Binding ExcelFile, Mode=OneWay}">
<excel:Excel.Resources>
<Style TargetType="{x:Type excel:Excel}">
<Setter Property="Canvas.Left" Value="0" />
<Setter Property="Canvas.Top" Value="0" />
<Setter Property="Width" Value="{Binding ElementName=canvas, Path=ActualWidth, Mode=OneWay}" />
<Setter Property="Height" Value="{Binding ElementName=canvas, Path=ActualHeight, Mode=OneWay}" />
<Style.Triggers>
<DataTrigger Binding="{Binding ExcelFile, Mode=OneWay}" Value="">
<Setter Property="Canvas.Left" Value="-100" />
<Setter Property="Canvas.Top" Value="-100" />
<Setter Property="Width" Value="10" />
<Setter Property="Height" Value="10" />
</DataTrigger>
</Style.Triggers>
</Style>
</excel:Excel.Resources>
</excel:Excel>
<word:Word File="{Binding WordFile, Mode=OneWay}">
<word:Word.Resources>
<Style TargetType="{x:Type word:Word}">
<Setter Property="Canvas.Left" Value="0" />
<Setter Property="Canvas.Top" Value="0" />
<Setter Property="Width" Value="{Binding ElementName=canvas, Path=ActualWidth, Mode=OneWay}" />
<Setter Property="Height" Value="{Binding ElementName=canvas, Path=ActualHeight, Mode=OneWay}" />
<Style.Triggers>
<DataTrigger Binding="{Binding WordFile, Mode=OneWay}" Value="">
<Setter Property="Canvas.Left" Value="-100" />
<Setter Property="Canvas.Top" Value="-100" />
<Setter Property="Width" Value="10" />
<Setter Property="Height" Value="10" />
</DataTrigger>
</Style.Triggers>
</Style>
</word:Word.Resources>
</word:Word>
</Canvas>
</Grid>
</Window>
Так как Z-index отказывается работать как ожидается, пришлось сделать переключение между Word и Excel немного необычным способом - прятать ненужное приложение за границу экрана, а необходимое в данный момент выводить на экран.
ViewModel окна предельно прост и не требует пояснений:
class MainWindowViewModel : INotifyPropertyChanged
{
#region Свойства
private string browserFile;
private string excelFile;
private string wordFile;
#endregion
#region Конструктор
public MainWindowViewModel()
{
this.excelFile = string.Empty;
this.wordFile = string.Empty;
}
#endregion
#region Общие свойства
public string BrowserFile
{
get { return this.browserFile; }
set
{
if (this.browserFile != value)
{
this.browserFile = value;
this.BrowserFileChanged();
this.OnPropertyChanged("BrowserFile");
}
}
}
public string ExcelFile
{
get { return this.excelFile; }
private set
{
if (this.excelFile != value)
{
this.excelFile = value;
this.OnPropertyChanged("ExcelFile");
}
}
}
public string WordFile
{
get { return this.wordFile; }
private set
{
if (this.wordFile != value)
{
this.wordFile = value;
this.OnPropertyChanged("WordFile");
}
}
}
#endregion
#region Методы
private void BrowserFileChanged()
{
if (this.browserFile.EndsWith(".xls") || this.browserFile.EndsWith(".xlsx"))
{
this.ExcelFile = this.browserFile;
this.WordFile = string.Empty;
}
else if (this.browserFile.EndsWith(".doc") || this.browserFile.EndsWith(".docx") || this.browserFile.EndsWith(".rtf") || this.browserFile.EndsWith(".txt"))
{
this.ExcelFile = string.Empty;
this.WordFile = this.browserFile;
}
else
{
this.ExcelFile = string.Empty;
this.WordFile = string.Empty;
}
}
#endregion
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged == null)
return;
else
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Навигацию по директориям компьютера организуем с помощью TreeView
<UserControl x:Class="OfficeControlsTestApp.Controls.TreeViewFolderBrowser"
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:converters="clr-namespace:OfficeControlsTestApp.Converters"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<TreeView DockPanel.Dock="Left"
x:Name="browser"
Background="#FFFFFFFF" BorderBrush="#FFFFFFFF" Foreground="#FFFFFFFF">
<TreeView.Resources>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Margin="0,2"
Width="20" Height="20"
Stretch="Fill"
Source="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeViewItem}}, Path=Tag, Converter={x:Static converters:HeaderToImageConverter.Instance}}" />
<TextBlock Margin="5,0"
VerticalAlignment="Center"
Text="{Binding}" />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TreeView.Resources>
</TreeView>
</UserControl>
CodeBehind
public partial class TreeViewFolderBrowser : UserControl
{
private static object DUMMYNODE = null;
public TreeViewFolderBrowser()
{
InitializeComponent();
this.Loaded += TreeViewFolderBrowser_Loaded;
}
/// <summary>
/// При загрузке контрола
/// </summary>
void TreeViewFolderBrowser_Loaded(object sender, RoutedEventArgs e)
{
foreach (string drives in Directory.GetLogicalDrives())
{
TreeViewItem item = new TreeViewItem();
item.Header = drives;
item.Tag = drives;
item.FontWeight = FontWeights.Normal;
item.Items.Add(TreeViewFolderBrowser.DUMMYNODE);
item.Expanded += browser_FolderExpanded;
browser.Items.Add(item);
}
}
/// <summary>
/// При раскрытии папки
/// </summary>
void browser_FolderExpanded(object sender, RoutedEventArgs e)
{
TreeViewItem item = (TreeViewItem)sender;
if (item.Items.Count == 1 && item.Items[0] == TreeViewFolderBrowser.DUMMYNODE)
{
item.Items.Clear();
try
{
foreach (string folder in Directory.GetDirectories(item.Tag as string))
{
TreeViewItem subitem = new TreeViewItem();
subitem.Header = folder.Substring(folder.LastIndexOf("\\") + 1);
subitem.Tag = folder;
subitem.FontWeight = FontWeights.Normal;
subitem.Items.Add(TreeViewFolderBrowser.DUMMYNODE);
subitem.Expanded += browser_FolderExpanded;
item.Items.Add(subitem);
}
foreach (string file in Directory.GetFiles(item.Tag as string))
{
TreeViewItem subitem = new TreeViewItem();
subitem.Header = file.Substring(file.LastIndexOf("\\") + 1);
subitem.Tag = file;
subitem.FontWeight = FontWeights.Normal;
subitem.Selected += new RoutedEventHandler(browserFile_Selected);
item.Items.Add(subitem);
}
}
catch (Exception) { }
}
}
/// <summary>
/// При выборе файла
/// </summary>
void browserFile_Selected(object sender, RoutedEventArgs e)
{
TreeViewItem item = sender as TreeViewItem;
if (item == null)
return;
this.File = (item.Tag as string);
}
/// <summary>
/// Путь к файлу
/// </summary>
public string File
{
get { return (string)this.GetValue(FileProperty); }
set { this.SetValue(FileProperty, value); }
}
public static readonly DependencyProperty FileProperty = DependencyProperty.Register("File", typeof(string), typeof(TreeViewFolderBrowser), new PropertyMetadata(string.Empty));
}
Конвертер, используемый TreeView, выдаёт на основании пути и расширения отдельного элемента подходящую картинку
[ValueConversion(typeof(string), typeof(bool))]
class HeaderToImageConverter : IValueConverter
{
public static HeaderToImageConverter Instance = new HeaderToImageConverter();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string path = (value as string).ToLower();
if (path.EndsWith(@"\"))
{
Uri uri = new Uri("pack://application:,,,/Images/drive.png");
BitmapImage source = new BitmapImage(uri);
return source;
}
else if (path.EndsWith(".xls") || path.EndsWith(".xlsx"))
{
Uri uri = new Uri("pack://application:,,,/Images/excel.png");
BitmapImage source = new BitmapImage(uri);
return source;
}
else if (path.EndsWith(".doc") || path.EndsWith(".docx") || path.EndsWith(".rtf"))
{
Uri uri = new Uri("pack://application:,,,/Images/word.png");
BitmapImage source = new BitmapImage(uri);
return source;
}
else if (path.EndsWith(".txt"))
{
Uri uri = new Uri("pack://application:,,,/Images/text.png");
BitmapImage source = new BitmapImage(uri);
return source;
}
else if (path.EndsWith(".bmp"))
{
Uri uri = new Uri("pack://application:,,,/Images/image-bmp.png");
BitmapImage source = new BitmapImage(uri);
return source;
}
else if (path.EndsWith(".gif"))
{
Uri uri = new Uri("pack://application:,,,/Images/image-gif.png");
BitmapImage source = new BitmapImage(uri);
return source;
}
else if (path.EndsWith(".jpg") || path.EndsWith(".jpeg"))
{
Uri uri = new Uri("pack://application:,,,/Images/image-jpeg.png");
BitmapImage source = new BitmapImage(uri);
return source;
}
else if (path.EndsWith(".png"))
{
Uri uri = new Uri("pack://application:,,,/Images/image-png.png");
BitmapImage source = new BitmapImage(uri);
return source;
}
else
{
Uri uri = File.Exists(path) ? new Uri("pack://application:,,,/Images/unknown.png") : new Uri("pack://application:,,,/Images/folder.png");
BitmapImage source = new BitmapImage(uri);
return source;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException("Cannot convert back");
}
}
Запускаем приложение
Выбираем документ другого типа. Excel прячется, открывается Word
При изменении размеров окна также меняется и размер встроенного Word'а