RichTextBox 中的图像在调整大小后变得模糊
Image in RichTextBox is blurry after resizing
我有一个 RichTextBox。我通过复制+粘贴插入图像。我使用装饰器来调整图像大小。 RTB 将被保存,同时为 RTB 文本使用一个 XAML 文件,为每个图像分别使用一个 PNG 文件。
使用装饰器和 SAVE 调整图像大小后,图像变得模糊。对于每次保存,图像变得越来越模糊 - 即使没有进一步调整大小。
请问我能做什么?
<Window x:Name="windowRTB" x:Class="RichTextBox.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"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<DockPanel Name="mainPanel">
<ToolBar Name="mainToolBar" Height="30" DockPanel.Dock="Top" Background="{x:Null}">
<Button Click="SaveContent" Loaded="Button_Loaded" Content="SAVE" FontWeight="Bold"/>
</ToolBar>
<RichTextBox Name="rtb" IsDocumentEnabled="True" AcceptsTab="True" IsReadOnly="False" TextChanged="Rtb_TextChanged">
</RichTextBox>
</DockPanel>
</Grid>
public partial class MainWindow : Window
{
const string gc_pathAppDataXAML = @"C:\Users\RB\Documents\XAML\";
const string gc_pathAppDataPNG = @"C:\Users\RB\Documents\PNG\";
public MainWindow()
{
InitializeComponent();
}
private void Rtb_TextChanged(object sender, TextChangedEventArgs e)
{
foreach (Image img in rtb.FindChildren<Image>())
{
img.Loaded += delegate
{
img.PreviewMouseDown += new MouseButtonEventHandler(Image_PreviewMouseDown);
};
}
}
private void Image_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (rtb.IsReadOnly == false)
{
Image img = (Image)e.OriginalSource;
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(img);
if (adornerLayer != null)
{
adornerLayer.Add(new ResizingAdorner(img));
}
}
}
private void SaveContent(object sender, RoutedEventArgs e)
{
SaveXamlPackage(gc_pathAppDataXAML + "RTB-Test" + ".xaml");
}
private void SaveXamlPackage(string iv_fileName)
{
try
{
//Separately saving the images
ExtractImages();
//Save RTB content with placeholders for the images
TextRange range1;
FileStream fStream1;
range1 = new TextRange(rtb.Document.ContentStart, rtb.Document.ContentEnd);
fStream1 = new FileStream(iv_fileName, FileMode.Create);
range1.Save(fStream1, DataFormats.XamlPackage);
fStream1.Close();
//Load RichTextBox
LoadXamlPackage(iv_fileName);
}
catch (Exception error)
{
MessageBox.Show(error.Message);
return;
}
}
private void ExtractImages() //Separately saving the images
{
Image Image;
int lv_cnt = 0;
foreach (Block block in rtb.Document.Blocks)
{
if (block is Paragraph)
{
Paragraph paragraph = (Paragraph)block;
foreach (Inline inline in paragraph.Inlines)
{
if (inline is InlineUIContainer)
{
InlineUIContainer uiContainer = (InlineUIContainer)inline;
if (uiContainer.Child is System.Windows.Controls.Image)
{
lv_cnt++;
Image = (Image)uiContainer.Child;
SaveToPng(Image, gc_pathAppDataPNG + "RTB-Test".ToString() + "-" + lv_cnt + ".png");
Image.Source = new BitmapImage(
new Uri("pack://application:,,,/RichTextBox;component/RTB_Platzhalter.png"));
}
}
}
}
else if (block is BlockUIContainer)
{
BlockUIContainer container = (BlockUIContainer)block;
if (container.Child is Image)
{
lv_cnt++;
Image = (Image)container.Child;
SaveToPng(Image, gc_pathAppDataPNG + "RTB-Test".ToString() + "-" + lv_cnt + ".png");
Image.Source = new BitmapImage(
new Uri("pack://application:,,,/RichTextBox;component/RTB_Platzhalter.png"));
}
}
}
void SaveToPng(FrameworkElement visual, string fileName)
{
var encoder = new PngBitmapEncoder();
SaveUsingEncoder(visual, fileName, encoder);
}
void SaveUsingEncoder(FrameworkElement visual, string fileName, BitmapEncoder encoder)
{
RenderTargetBitmap bitmap = new RenderTargetBitmap((int)visual.ActualWidth, (int)visual.ActualHeight, 96, 96, PixelFormats.Pbgra32);
Size visualSize = new Size(visual.ActualWidth, visual.ActualHeight);
visual.Measure(visualSize);
visual.Arrange(new Rect(visualSize));
bitmap.Render(visual);
BitmapFrame frame = BitmapFrame.Create(bitmap);
encoder.Frames.Add(frame);
using (FileStream stream = File.Create(fileName))
{
encoder.Save(stream);
}
}
}
private void LoadContent() //While creating instance of windowRTB
{
LoadXamlPackage(gc_pathAppDataXAML + "RTB-Test".ToString() + ".xaml");
}
private void LoadXamlPackage(string iv_fileName)
{
try
{
TextRange range;
FileStream fStream;
if (File.Exists(iv_fileName))
{
range = new TextRange(rtb.Document.ContentStart, rtb.Document.ContentEnd);
fStream = new FileStream(iv_fileName, FileMode.OpenOrCreate);
range.Load(fStream, DataFormats.XamlPackage);
fStream.Close();
}
LoadImages();
}
catch (Exception error)
{
MessageBox.Show(error.Message);
return;
}
}
private void LoadImages() //Reloading of separately saved images
{
Image Image;
int lv_cnt = 0;
foreach (Block block in rtb.Document.Blocks)
{
if (block is Paragraph)
{
Paragraph paragraph = (Paragraph)block;
foreach (Inline inline in paragraph.Inlines)
{
if (inline is InlineUIContainer)
{
InlineUIContainer uiContainer = (InlineUIContainer)inline;
if (uiContainer.Child is System.Windows.Controls.Image)
{
lv_cnt++;
Image = (System.Windows.Controls.Image)uiContainer.Child;
Image.Source = null;
using (FileStream fs = new FileStream(gc_pathAppDataPNG + "RTB-Test".ToString() + "-" + lv_cnt + ".png", FileMode.Open))
{
BitmapImage imageSource = new BitmapImage();
imageSource.BeginInit();
imageSource.CacheOption = BitmapCacheOption.OnLoad;
imageSource.StreamSource = fs;
imageSource.EndInit();
Image.Source = imageSource;
}
}
}
}
}
else if (block is BlockUIContainer)
{
BlockUIContainer container = (BlockUIContainer)block;
if (container.Child is System.Windows.Controls.Image)
{
lv_cnt++;
Image = (System.Windows.Controls.Image)container.Child;
using (FileStream fs = new FileStream(gc_pathAppDataPNG + "RTB-Test".ToString() + "-" + lv_cnt + ".png", FileMode.Open))
{
BitmapImage imageSource = new BitmapImage();
imageSource.BeginInit();
imageSource.CacheOption = BitmapCacheOption.OnLoad;
imageSource.StreamSource = fs;
imageSource.EndInit();
Image.Source = imageSource;
}
}
}
}
}
private void Button_Loaded(object sender, RoutedEventArgs e)
{
LoadContent();
}
}
public class ResizingAdorner : Adorner
{
// Resizing adorner uses Thumbs for visual elements.
// The Thumbs have built-in mouse input handling.
Thumb topLeft, topRight, bottomLeft, bottomRight;
// To store and manage the adorner's visual children.
VisualCollection visualChildren;
// Initialize the ResizingAdorner.
public ResizingAdorner(UIElement adornedElement) : base(adornedElement)
{
visualChildren = new VisualCollection(this);
// Call a helper method to initialize the Thumbs
// with a customized cursors.
BuildAdornerCorner(ref topLeft, Cursors.SizeNWSE);
BuildAdornerCorner(ref topRight, Cursors.SizeNESW);
BuildAdornerCorner(ref bottomLeft, Cursors.SizeNESW);
BuildAdornerCorner(ref bottomRight, Cursors.SizeNWSE);
// Add handlers for resizing.
bottomLeft.DragDelta += new DragDeltaEventHandler(HandleBottomLeft);
bottomRight.DragDelta += new DragDeltaEventHandler(HandleBottomRight);
topLeft.DragDelta += new DragDeltaEventHandler(HandleTopLeft);
topRight.DragDelta += new DragDeltaEventHandler(HandleTopRight);
}
// Handler for resizing from the bottom-right.
void HandleBottomRight(object sender, DragDeltaEventArgs args)
{
FrameworkElement adornedElement = this.AdornedElement as FrameworkElement;
Thumb hitThumb = sender as Thumb;
if (adornedElement == null || hitThumb == null) return;
FrameworkElement parentElement = adornedElement.Parent as FrameworkElement;
// Ensure that the Width and Height are properly initialized after the resize.
EnforceSize(adornedElement);
// Change the size by the amount the user drags the mouse, as long as it's larger
// than the width or height of an adorner, respectively.
double lv_oldWidth = adornedElement.Width;
adornedElement.Width = Math.Max(adornedElement.Width + args.HorizontalChange, hitThumb.DesiredSize.Width);
adornedElement.Height = adornedElement.Height * adornedElement.Width / lv_oldWidth;
}
// Handler for resizing from the bottom-left.
void HandleBottomLeft(object sender, DragDeltaEventArgs args)
{
FrameworkElement adornedElement = AdornedElement as FrameworkElement;
Thumb hitThumb = sender as Thumb;
if (adornedElement == null || hitThumb == null) return;
// Ensure that the Width and Height are properly initialized after the resize.
EnforceSize(adornedElement);
// Change the size by the amount the user drags the mouse, as long as it's larger
// than the width or height of an adorner, respectively.
double lv_oldWidth = adornedElement.Width;
adornedElement.Width = Math.Max(adornedElement.Width - args.HorizontalChange, hitThumb.DesiredSize.Width);
adornedElement.Height = adornedElement.Height * adornedElement.Width / lv_oldWidth;
}
// Handler for resizing from the top-right.
void HandleTopRight(object sender, DragDeltaEventArgs args)
{
FrameworkElement adornedElement = this.AdornedElement as FrameworkElement;
Thumb hitThumb = sender as Thumb;
if (adornedElement == null || hitThumb == null) return;
FrameworkElement parentElement = adornedElement.Parent as FrameworkElement;
// Ensure that the Width and Height are properly initialized after the resize.
EnforceSize(adornedElement);
// Change the size by the amount the user drags the mouse, as long as it's larger
// than the width or height of an adorner, respectively.
double lv_oldWidth = adornedElement.Width;
adornedElement.Width = Math.Max(adornedElement.Width + args.HorizontalChange, hitThumb.DesiredSize.Width);
adornedElement.Height = adornedElement.Height * adornedElement.Width / lv_oldWidth;
}
// Handler for resizing from the top-left.
void HandleTopLeft(object sender, DragDeltaEventArgs args)
{
FrameworkElement adornedElement = AdornedElement as FrameworkElement;
Thumb hitThumb = sender as Thumb;
if (adornedElement == null || hitThumb == null) return;
// Ensure that the Width and Height are properly initialized after the resize.
EnforceSize(adornedElement);
// Change the size by the amount the user drags the mouse, as long as it's larger
// than the width or height of an adorner, respectively.
double lv_oldWidth = adornedElement.Width;
adornedElement.Width = Math.Max(adornedElement.Width - args.HorizontalChange, hitThumb.DesiredSize.Width);
adornedElement.Height = adornedElement.Height * adornedElement.Width / lv_oldWidth;
}
// Arrange the Adorners.
protected override Size ArrangeOverride(Size finalSize)
{
// desiredWidth and desiredHeight are the width and height of the element that's being adorned.
// These will be used to place the ResizingAdorner at the corners of the adorned element.
double desiredWidth = AdornedElement.DesiredSize.Width;
double desiredHeight = AdornedElement.DesiredSize.Height;
// adornerWidth & adornerHeight are used for placement as well.
double adornerWidth = this.DesiredSize.Width;
double adornerHeight = this.DesiredSize.Height;
topLeft.Arrange(new Rect(-adornerWidth / 2, -adornerHeight / 2, adornerWidth, adornerHeight));
topRight.Arrange(new Rect(desiredWidth - adornerWidth / 2, -adornerHeight / 2, adornerWidth, adornerHeight));
bottomLeft.Arrange(new Rect(-adornerWidth / 2, desiredHeight - adornerHeight / 2, adornerWidth, adornerHeight));
bottomRight.Arrange(new Rect(desiredWidth - adornerWidth / 2, desiredHeight - adornerHeight / 2, adornerWidth, adornerHeight));
// Return the final size.
return finalSize;
}
// Helper method to instantiate the corner Thumbs, set the Cursor property,
// set some appearance properties, and add the elements to the visual tree.
void BuildAdornerCorner(ref Thumb cornerThumb, Cursor customizedCursor)
{
if (cornerThumb != null) return;
cornerThumb = new Thumb();
// Set some arbitrary visual characteristics.
cornerThumb.Cursor = customizedCursor;
cornerThumb.Height = cornerThumb.Width = 10;
cornerThumb.Opacity = 0.40;
cornerThumb.Background = new SolidColorBrush(Colors.MediumBlue);
visualChildren.Add(cornerThumb);
}
// This method ensures that the Widths and Heights are initialized. Sizing to content produces
// Width and Height values of Double.NaN. Because this Adorner explicitly resizes, the Width and Height
// need to be set first. It also sets the maximum size of the adorned element.
void EnforceSize(FrameworkElement adornedElement)
{
if (adornedElement.Width.Equals(Double.NaN))
adornedElement.Width = adornedElement.DesiredSize.Width;
if (adornedElement.Height.Equals(Double.NaN))
adornedElement.Height = adornedElement.DesiredSize.Height;
FrameworkElement parent = adornedElement.Parent as FrameworkElement;
if (parent != null)
{
adornedElement.MaxHeight = parent.ActualHeight;
adornedElement.MaxWidth = parent.ActualWidth;
}
}
// Override the VisualChildrenCount and GetVisualChild properties to interface with
// the adorner's visual collection.
protected override int VisualChildrenCount { get { return visualChildren.Count; } }
protected override Visual GetVisualChild(int index) { return visualChildren[index]; }
}
/// <summary>
/// Helper methods for UI-related tasks.
/// </summary>
public static class TreeHelper
{
#region find parent
public static T TryFindParent<T>(this DependencyObject child)
where T : DependencyObject
{
//get parent item
DependencyObject parentObject = GetParentObject(child);
//we've reached the end of the tree
if (parentObject == null) return null;
//check if the parent matches the type we're looking for
T parent = parentObject as T;
if (parent != null)
{
return parent;
}
else
{
//use recursion to proceed with next level
return TryFindParent<T>(parentObject);
}
}
public static DependencyObject GetParentObject(this DependencyObject child)
{
if (child == null) return null;
//handle content elements separately
ContentElement contentElement = child as ContentElement;
if (contentElement != null)
{
DependencyObject parent = ContentOperations.GetParent(contentElement);
if (parent != null) return parent;
FrameworkContentElement fce = contentElement as FrameworkContentElement;
return fce != null ? fce.Parent : null;
}
//also try searching for parent in framework elements (such as DockPanel, etc)
FrameworkElement frameworkElement = child as FrameworkElement;
if (frameworkElement != null)
{
DependencyObject parent = frameworkElement.Parent;
if (parent != null) return parent;
}
//if it's not a ContentElement/FrameworkElement, rely on VisualTreeHelper
return VisualTreeHelper.GetParent(child);
}
#endregion
#region find children
public static IEnumerable<T> FindChildren<T>(this DependencyObject source) where T : DependencyObject
{
if (source != null)
{
var childs = GetChildObjects(source);
foreach (DependencyObject child in childs)
{
//analyze if children match the requested type
if (child != null && child is T)
{
yield return (T)child;
}
//recurse tree
foreach (T descendant in FindChildren<T>(child))
{
yield return descendant;
}
}
}
}
public static IEnumerable<DependencyObject> GetChildObjects(this DependencyObject parent)
{
if (parent == null) yield break;
if (parent is ContentElement || parent is FrameworkElement)
{
//use the logical tree for content / framework elements
foreach (object obj in LogicalTreeHelper.GetChildren(parent))
{
var depObj = obj as DependencyObject;
if (depObj != null) yield return (DependencyObject)obj;
}
}
else
{
//use the visual tree per default
int count = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < count; i++)
{
yield return VisualTreeHelper.GetChild(parent, i);
}
}
}
#endregion
#region find from point
public static T TryFindFromPoint<T>(UIElement reference, Point point)
where T : DependencyObject
{
DependencyObject element = reference.InputHitTest(point) as DependencyObject;
if (element == null) return null;
else if (element is T) return (T)element;
else return TryFindParent<T>(element);
}
#endregion
}
上面代码中的问题是使用视觉对象的大小而不是源图像大小(及其内容)。因此,当您更改文档中图像的大小(无论是减小还是增大大小)然后保存文档时,保存的图像总是会失去质量。
解决方案是使用原始大小和内容保存图像。
因此,进行更改以使用图像源 ( (+1)):
void SaveToPng(Image image, string fileName)
{
var encoder = new PngBitmapEncoder();
SaveUsingEncoder(image, fileName, encoder);
}
void SaveUsingEncoder(Image image, string fileName, BitmapEncoder encoder)
{
encoder.Frames.Add(BitmapFrame.Create((BitmapSource)image.Source));
using (FileStream stream = new FileStream(fileName, FileMode.Create))
encoder.Save(stream);
}
我有一个 RichTextBox。我通过复制+粘贴插入图像。我使用装饰器来调整图像大小。 RTB 将被保存,同时为 RTB 文本使用一个 XAML 文件,为每个图像分别使用一个 PNG 文件。 使用装饰器和 SAVE 调整图像大小后,图像变得模糊。对于每次保存,图像变得越来越模糊 - 即使没有进一步调整大小。 请问我能做什么?
<Window x:Name="windowRTB" x:Class="RichTextBox.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"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<DockPanel Name="mainPanel">
<ToolBar Name="mainToolBar" Height="30" DockPanel.Dock="Top" Background="{x:Null}">
<Button Click="SaveContent" Loaded="Button_Loaded" Content="SAVE" FontWeight="Bold"/>
</ToolBar>
<RichTextBox Name="rtb" IsDocumentEnabled="True" AcceptsTab="True" IsReadOnly="False" TextChanged="Rtb_TextChanged">
</RichTextBox>
</DockPanel>
</Grid>
public partial class MainWindow : Window
{
const string gc_pathAppDataXAML = @"C:\Users\RB\Documents\XAML\";
const string gc_pathAppDataPNG = @"C:\Users\RB\Documents\PNG\";
public MainWindow()
{
InitializeComponent();
}
private void Rtb_TextChanged(object sender, TextChangedEventArgs e)
{
foreach (Image img in rtb.FindChildren<Image>())
{
img.Loaded += delegate
{
img.PreviewMouseDown += new MouseButtonEventHandler(Image_PreviewMouseDown);
};
}
}
private void Image_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (rtb.IsReadOnly == false)
{
Image img = (Image)e.OriginalSource;
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(img);
if (adornerLayer != null)
{
adornerLayer.Add(new ResizingAdorner(img));
}
}
}
private void SaveContent(object sender, RoutedEventArgs e)
{
SaveXamlPackage(gc_pathAppDataXAML + "RTB-Test" + ".xaml");
}
private void SaveXamlPackage(string iv_fileName)
{
try
{
//Separately saving the images
ExtractImages();
//Save RTB content with placeholders for the images
TextRange range1;
FileStream fStream1;
range1 = new TextRange(rtb.Document.ContentStart, rtb.Document.ContentEnd);
fStream1 = new FileStream(iv_fileName, FileMode.Create);
range1.Save(fStream1, DataFormats.XamlPackage);
fStream1.Close();
//Load RichTextBox
LoadXamlPackage(iv_fileName);
}
catch (Exception error)
{
MessageBox.Show(error.Message);
return;
}
}
private void ExtractImages() //Separately saving the images
{
Image Image;
int lv_cnt = 0;
foreach (Block block in rtb.Document.Blocks)
{
if (block is Paragraph)
{
Paragraph paragraph = (Paragraph)block;
foreach (Inline inline in paragraph.Inlines)
{
if (inline is InlineUIContainer)
{
InlineUIContainer uiContainer = (InlineUIContainer)inline;
if (uiContainer.Child is System.Windows.Controls.Image)
{
lv_cnt++;
Image = (Image)uiContainer.Child;
SaveToPng(Image, gc_pathAppDataPNG + "RTB-Test".ToString() + "-" + lv_cnt + ".png");
Image.Source = new BitmapImage(
new Uri("pack://application:,,,/RichTextBox;component/RTB_Platzhalter.png"));
}
}
}
}
else if (block is BlockUIContainer)
{
BlockUIContainer container = (BlockUIContainer)block;
if (container.Child is Image)
{
lv_cnt++;
Image = (Image)container.Child;
SaveToPng(Image, gc_pathAppDataPNG + "RTB-Test".ToString() + "-" + lv_cnt + ".png");
Image.Source = new BitmapImage(
new Uri("pack://application:,,,/RichTextBox;component/RTB_Platzhalter.png"));
}
}
}
void SaveToPng(FrameworkElement visual, string fileName)
{
var encoder = new PngBitmapEncoder();
SaveUsingEncoder(visual, fileName, encoder);
}
void SaveUsingEncoder(FrameworkElement visual, string fileName, BitmapEncoder encoder)
{
RenderTargetBitmap bitmap = new RenderTargetBitmap((int)visual.ActualWidth, (int)visual.ActualHeight, 96, 96, PixelFormats.Pbgra32);
Size visualSize = new Size(visual.ActualWidth, visual.ActualHeight);
visual.Measure(visualSize);
visual.Arrange(new Rect(visualSize));
bitmap.Render(visual);
BitmapFrame frame = BitmapFrame.Create(bitmap);
encoder.Frames.Add(frame);
using (FileStream stream = File.Create(fileName))
{
encoder.Save(stream);
}
}
}
private void LoadContent() //While creating instance of windowRTB
{
LoadXamlPackage(gc_pathAppDataXAML + "RTB-Test".ToString() + ".xaml");
}
private void LoadXamlPackage(string iv_fileName)
{
try
{
TextRange range;
FileStream fStream;
if (File.Exists(iv_fileName))
{
range = new TextRange(rtb.Document.ContentStart, rtb.Document.ContentEnd);
fStream = new FileStream(iv_fileName, FileMode.OpenOrCreate);
range.Load(fStream, DataFormats.XamlPackage);
fStream.Close();
}
LoadImages();
}
catch (Exception error)
{
MessageBox.Show(error.Message);
return;
}
}
private void LoadImages() //Reloading of separately saved images
{
Image Image;
int lv_cnt = 0;
foreach (Block block in rtb.Document.Blocks)
{
if (block is Paragraph)
{
Paragraph paragraph = (Paragraph)block;
foreach (Inline inline in paragraph.Inlines)
{
if (inline is InlineUIContainer)
{
InlineUIContainer uiContainer = (InlineUIContainer)inline;
if (uiContainer.Child is System.Windows.Controls.Image)
{
lv_cnt++;
Image = (System.Windows.Controls.Image)uiContainer.Child;
Image.Source = null;
using (FileStream fs = new FileStream(gc_pathAppDataPNG + "RTB-Test".ToString() + "-" + lv_cnt + ".png", FileMode.Open))
{
BitmapImage imageSource = new BitmapImage();
imageSource.BeginInit();
imageSource.CacheOption = BitmapCacheOption.OnLoad;
imageSource.StreamSource = fs;
imageSource.EndInit();
Image.Source = imageSource;
}
}
}
}
}
else if (block is BlockUIContainer)
{
BlockUIContainer container = (BlockUIContainer)block;
if (container.Child is System.Windows.Controls.Image)
{
lv_cnt++;
Image = (System.Windows.Controls.Image)container.Child;
using (FileStream fs = new FileStream(gc_pathAppDataPNG + "RTB-Test".ToString() + "-" + lv_cnt + ".png", FileMode.Open))
{
BitmapImage imageSource = new BitmapImage();
imageSource.BeginInit();
imageSource.CacheOption = BitmapCacheOption.OnLoad;
imageSource.StreamSource = fs;
imageSource.EndInit();
Image.Source = imageSource;
}
}
}
}
}
private void Button_Loaded(object sender, RoutedEventArgs e)
{
LoadContent();
}
}
public class ResizingAdorner : Adorner
{
// Resizing adorner uses Thumbs for visual elements.
// The Thumbs have built-in mouse input handling.
Thumb topLeft, topRight, bottomLeft, bottomRight;
// To store and manage the adorner's visual children.
VisualCollection visualChildren;
// Initialize the ResizingAdorner.
public ResizingAdorner(UIElement adornedElement) : base(adornedElement)
{
visualChildren = new VisualCollection(this);
// Call a helper method to initialize the Thumbs
// with a customized cursors.
BuildAdornerCorner(ref topLeft, Cursors.SizeNWSE);
BuildAdornerCorner(ref topRight, Cursors.SizeNESW);
BuildAdornerCorner(ref bottomLeft, Cursors.SizeNESW);
BuildAdornerCorner(ref bottomRight, Cursors.SizeNWSE);
// Add handlers for resizing.
bottomLeft.DragDelta += new DragDeltaEventHandler(HandleBottomLeft);
bottomRight.DragDelta += new DragDeltaEventHandler(HandleBottomRight);
topLeft.DragDelta += new DragDeltaEventHandler(HandleTopLeft);
topRight.DragDelta += new DragDeltaEventHandler(HandleTopRight);
}
// Handler for resizing from the bottom-right.
void HandleBottomRight(object sender, DragDeltaEventArgs args)
{
FrameworkElement adornedElement = this.AdornedElement as FrameworkElement;
Thumb hitThumb = sender as Thumb;
if (adornedElement == null || hitThumb == null) return;
FrameworkElement parentElement = adornedElement.Parent as FrameworkElement;
// Ensure that the Width and Height are properly initialized after the resize.
EnforceSize(adornedElement);
// Change the size by the amount the user drags the mouse, as long as it's larger
// than the width or height of an adorner, respectively.
double lv_oldWidth = adornedElement.Width;
adornedElement.Width = Math.Max(adornedElement.Width + args.HorizontalChange, hitThumb.DesiredSize.Width);
adornedElement.Height = adornedElement.Height * adornedElement.Width / lv_oldWidth;
}
// Handler for resizing from the bottom-left.
void HandleBottomLeft(object sender, DragDeltaEventArgs args)
{
FrameworkElement adornedElement = AdornedElement as FrameworkElement;
Thumb hitThumb = sender as Thumb;
if (adornedElement == null || hitThumb == null) return;
// Ensure that the Width and Height are properly initialized after the resize.
EnforceSize(adornedElement);
// Change the size by the amount the user drags the mouse, as long as it's larger
// than the width or height of an adorner, respectively.
double lv_oldWidth = adornedElement.Width;
adornedElement.Width = Math.Max(adornedElement.Width - args.HorizontalChange, hitThumb.DesiredSize.Width);
adornedElement.Height = adornedElement.Height * adornedElement.Width / lv_oldWidth;
}
// Handler for resizing from the top-right.
void HandleTopRight(object sender, DragDeltaEventArgs args)
{
FrameworkElement adornedElement = this.AdornedElement as FrameworkElement;
Thumb hitThumb = sender as Thumb;
if (adornedElement == null || hitThumb == null) return;
FrameworkElement parentElement = adornedElement.Parent as FrameworkElement;
// Ensure that the Width and Height are properly initialized after the resize.
EnforceSize(adornedElement);
// Change the size by the amount the user drags the mouse, as long as it's larger
// than the width or height of an adorner, respectively.
double lv_oldWidth = adornedElement.Width;
adornedElement.Width = Math.Max(adornedElement.Width + args.HorizontalChange, hitThumb.DesiredSize.Width);
adornedElement.Height = adornedElement.Height * adornedElement.Width / lv_oldWidth;
}
// Handler for resizing from the top-left.
void HandleTopLeft(object sender, DragDeltaEventArgs args)
{
FrameworkElement adornedElement = AdornedElement as FrameworkElement;
Thumb hitThumb = sender as Thumb;
if (adornedElement == null || hitThumb == null) return;
// Ensure that the Width and Height are properly initialized after the resize.
EnforceSize(adornedElement);
// Change the size by the amount the user drags the mouse, as long as it's larger
// than the width or height of an adorner, respectively.
double lv_oldWidth = adornedElement.Width;
adornedElement.Width = Math.Max(adornedElement.Width - args.HorizontalChange, hitThumb.DesiredSize.Width);
adornedElement.Height = adornedElement.Height * adornedElement.Width / lv_oldWidth;
}
// Arrange the Adorners.
protected override Size ArrangeOverride(Size finalSize)
{
// desiredWidth and desiredHeight are the width and height of the element that's being adorned.
// These will be used to place the ResizingAdorner at the corners of the adorned element.
double desiredWidth = AdornedElement.DesiredSize.Width;
double desiredHeight = AdornedElement.DesiredSize.Height;
// adornerWidth & adornerHeight are used for placement as well.
double adornerWidth = this.DesiredSize.Width;
double adornerHeight = this.DesiredSize.Height;
topLeft.Arrange(new Rect(-adornerWidth / 2, -adornerHeight / 2, adornerWidth, adornerHeight));
topRight.Arrange(new Rect(desiredWidth - adornerWidth / 2, -adornerHeight / 2, adornerWidth, adornerHeight));
bottomLeft.Arrange(new Rect(-adornerWidth / 2, desiredHeight - adornerHeight / 2, adornerWidth, adornerHeight));
bottomRight.Arrange(new Rect(desiredWidth - adornerWidth / 2, desiredHeight - adornerHeight / 2, adornerWidth, adornerHeight));
// Return the final size.
return finalSize;
}
// Helper method to instantiate the corner Thumbs, set the Cursor property,
// set some appearance properties, and add the elements to the visual tree.
void BuildAdornerCorner(ref Thumb cornerThumb, Cursor customizedCursor)
{
if (cornerThumb != null) return;
cornerThumb = new Thumb();
// Set some arbitrary visual characteristics.
cornerThumb.Cursor = customizedCursor;
cornerThumb.Height = cornerThumb.Width = 10;
cornerThumb.Opacity = 0.40;
cornerThumb.Background = new SolidColorBrush(Colors.MediumBlue);
visualChildren.Add(cornerThumb);
}
// This method ensures that the Widths and Heights are initialized. Sizing to content produces
// Width and Height values of Double.NaN. Because this Adorner explicitly resizes, the Width and Height
// need to be set first. It also sets the maximum size of the adorned element.
void EnforceSize(FrameworkElement adornedElement)
{
if (adornedElement.Width.Equals(Double.NaN))
adornedElement.Width = adornedElement.DesiredSize.Width;
if (adornedElement.Height.Equals(Double.NaN))
adornedElement.Height = adornedElement.DesiredSize.Height;
FrameworkElement parent = adornedElement.Parent as FrameworkElement;
if (parent != null)
{
adornedElement.MaxHeight = parent.ActualHeight;
adornedElement.MaxWidth = parent.ActualWidth;
}
}
// Override the VisualChildrenCount and GetVisualChild properties to interface with
// the adorner's visual collection.
protected override int VisualChildrenCount { get { return visualChildren.Count; } }
protected override Visual GetVisualChild(int index) { return visualChildren[index]; }
}
/// <summary>
/// Helper methods for UI-related tasks.
/// </summary>
public static class TreeHelper
{
#region find parent
public static T TryFindParent<T>(this DependencyObject child)
where T : DependencyObject
{
//get parent item
DependencyObject parentObject = GetParentObject(child);
//we've reached the end of the tree
if (parentObject == null) return null;
//check if the parent matches the type we're looking for
T parent = parentObject as T;
if (parent != null)
{
return parent;
}
else
{
//use recursion to proceed with next level
return TryFindParent<T>(parentObject);
}
}
public static DependencyObject GetParentObject(this DependencyObject child)
{
if (child == null) return null;
//handle content elements separately
ContentElement contentElement = child as ContentElement;
if (contentElement != null)
{
DependencyObject parent = ContentOperations.GetParent(contentElement);
if (parent != null) return parent;
FrameworkContentElement fce = contentElement as FrameworkContentElement;
return fce != null ? fce.Parent : null;
}
//also try searching for parent in framework elements (such as DockPanel, etc)
FrameworkElement frameworkElement = child as FrameworkElement;
if (frameworkElement != null)
{
DependencyObject parent = frameworkElement.Parent;
if (parent != null) return parent;
}
//if it's not a ContentElement/FrameworkElement, rely on VisualTreeHelper
return VisualTreeHelper.GetParent(child);
}
#endregion
#region find children
public static IEnumerable<T> FindChildren<T>(this DependencyObject source) where T : DependencyObject
{
if (source != null)
{
var childs = GetChildObjects(source);
foreach (DependencyObject child in childs)
{
//analyze if children match the requested type
if (child != null && child is T)
{
yield return (T)child;
}
//recurse tree
foreach (T descendant in FindChildren<T>(child))
{
yield return descendant;
}
}
}
}
public static IEnumerable<DependencyObject> GetChildObjects(this DependencyObject parent)
{
if (parent == null) yield break;
if (parent is ContentElement || parent is FrameworkElement)
{
//use the logical tree for content / framework elements
foreach (object obj in LogicalTreeHelper.GetChildren(parent))
{
var depObj = obj as DependencyObject;
if (depObj != null) yield return (DependencyObject)obj;
}
}
else
{
//use the visual tree per default
int count = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < count; i++)
{
yield return VisualTreeHelper.GetChild(parent, i);
}
}
}
#endregion
#region find from point
public static T TryFindFromPoint<T>(UIElement reference, Point point)
where T : DependencyObject
{
DependencyObject element = reference.InputHitTest(point) as DependencyObject;
if (element == null) return null;
else if (element is T) return (T)element;
else return TryFindParent<T>(element);
}
#endregion
}
上面代码中的问题是使用视觉对象的大小而不是源图像大小(及其内容)。因此,当您更改文档中图像的大小(无论是减小还是增大大小)然后保存文档时,保存的图像总是会失去质量。
解决方案是使用原始大小和内容保存图像。
因此,进行更改以使用图像源 (
void SaveToPng(Image image, string fileName)
{
var encoder = new PngBitmapEncoder();
SaveUsingEncoder(image, fileName, encoder);
}
void SaveUsingEncoder(Image image, string fileName, BitmapEncoder encoder)
{
encoder.Frames.Add(BitmapFrame.Create((BitmapSource)image.Source));
using (FileStream stream = new FileStream(fileName, FileMode.Create))
encoder.Save(stream);
}