How to convert a ViewBox to an ImageSource? - wpf

I'm using a Viewbox to create a set of icons that I will dynamically bind to a WPF view.
I'm binding to the resource name and using a Converter to convert the resource name to an ImageSource.
I know how to do it if the resource is a Path, but how to do it with a Viewbox?
This is how I convert the resource name, if the resource is a Path, to an ImageSource:
public class ResourceNameToImageSourceConverter : BaseValueConverter {
protected override ImageSource Convert(string value, System.Globalization.CultureInfo culture) {
var resource = new ResourceDictionary();
resource.Source = new Uri("pack://application:,,,/MyAssembly;component/MyResourceFolder/ImageResources.xaml", UriKind.Absolute);
var path = resource[value] as Path;
if (path != null) {
var geometry = path.Data;
var geometryDrawing = new GeometryDrawing();
geometryDrawing.Geometry = geometry;
var drawingImage = new DrawingImage(geometryDrawing);
geometryDrawing.Brush = path.Fill;
geometryDrawing.Pen = new Pen();
drawingImage.Freeze();
return drawingImage;
} else {
return null;
}
}
}
And this is what the Viewbox declaration looks like.
<Viewbox>
<Viewbox>
<Grid>
<Path>
...
</Path>
<Path>
...
</Path>
<Path>
...
</Path>
<Rectangle>
...
</Rectangle>
</Grid>
</Viewbox>
</Viewbox>

The Viewbox is a visual element, so you'd need to "render" it manually to a bitmap. This blog post shows how this is done, but the relevant code is:
private static BitmapSource CaptureScreen(Visual target, double dpiX, double dpiY) {
if (target == null)
return null;
Rect bounds = VisualTreeHelper.GetDescendantBounds(target);
RenderTargetBitmap rtb = new RenderTargetBitmap((int)(bounds.Width * dpiX / 96.0),
(int)(bounds.Height * dpiY / 96.0),
dpiX,
dpiY,
PixelFormats.Pbgra32);
DrawingVisual dv = new DrawingVisual();
using (DrawingContext ctx = dv.RenderOpen()) {
VisualBrush vb = new VisualBrush(target);
ctx.DrawRectangle(vb, null, new Rect(new Point(), bounds.Size));
}
rtb.Render(dv);
return rtb;
}

Related

when i run the code it throws invalid parameter exception how do i fix it?

so i decided to go on a different direction,which was saving the picture name on the database and copying the image in a folder inside my application.
while (reader.Read())
{
BitmapImage bitImg = new BitmapImage();
string fileName =
System.IO.Path.GetFileName(reader.GetString(12));
System.Windows.Controls.Image img = new
System.Windows.Controls.Image();
var impath="pack://application:,,,/TravelBuddyApp;component/images/" + fileName;
bitImg.UriSource = new Uri(impath,
UriKind.RelativeOrAbsolute);
img.Width = 100;
img.Height = 100;
img.Source = bitImg;
StackPanel sp = new StackPanel();
sp.Orientation = Orientation.Vertical;
sp.Children.Add(img);
listviewer.Items.Add(sp);
System.Drawing.Image is WinForms. Do not use it a WPF application.
Use classes derived from System.Window.Media.ImageSource, e.g. BitmapImage:
var bmp = new BitmapImage();
using (var memStm = new MemoryStream(imageBytes))
{
bmp.BeginInit();
bmp.StreamSource = memStm;
bmp.CacheOption = BitmapCacheOption.OnLoad;
bmp.EndInit();
}
listviewer.Items.Add(bmp);
In case listviewer is a ListBox or ListView, there should be an Image element in the ItemTemplate:
<ListBox x:Name="listviewer">
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

WPF. if pop up window appear, main window brightness decrease //code-behind

I need that if my pop up window appear (after click) , the main window brightness has to decrease, maybe someone know how to do it?
Example:
EDIT: I create canvas, but do not know how to use it, brightness need decrease then pop up appear.
code:
private void sample_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string path1 = System.AppDomain.CurrentDomain.BaseDirectory + "../../loader_bg.png";
string path2 = System.AppDomain.CurrentDomain.BaseDirectory + "../../loader.gif";
ImageBrush myBrush = new ImageBrush();
Image image = new Image();
image.Source = new BitmapImage(
new Uri(path1));
myBrush.ImageSource = image.Source;
Image ima = new Image();
MediaElement gif = new MediaElement();
ima.Source = new BitmapImage(new Uri(path1));
gif.Source=new Uri(path2);
gif.Height = 72;
gif.Width = 72;
var pop = new Popup
{
IsOpen = true,
StaysOpen = false,
AllowsTransparency = true,
VerticalOffset = 350,
HorizontalOffset = 700,
Height = 128,
Width = 128,
};
Canvas c=new Canvas();
c.Background=Brushes.Black;
c.Opacity = 0.6;
Grid p = new Grid();
p.Background = myBrush;
//p.Children.Add(ima);
//p.Children.Add(c);
p.Children.Add(gif);
pop.Child = p;
}
}
EDIT 2:
I have the same question only my code is change. Now I created new xaml.cs for pop up window, and try to achieve the same purpose, but I do not get the same (I talk about brightness decrease).
Its my new xaml.cs :
namespace uploader
{
/// <summary>
/// Interaction logic for PopupPanel.xaml
/// </summary>
public partial class PopupPanel : UserControl
{
private Popup _currentPopup;
public PopupPanel()
{
InitializeComponent();
string path1 = System.AppDomain.CurrentDomain.BaseDirectory + "../../loader_bg.png";
string path2 = System.AppDomain.CurrentDomain.BaseDirectory + "../../loader.gif";
ImageBrush myBrush = new ImageBrush();
Image image = new Image();
image.Source = new BitmapImage(new Uri(path1));
myBrush.ImageSource = image.Source;
MediaElement gif = new MediaElement();
gif.Source=new Uri(path2);
gif.Height = 72;
gif.Width = 72;
_currentPopup = new Popup
{
StaysOpen = false,
AllowsTransparency = true,
VerticalOffset = 350,
HorizontalOffset = 700,
Height = 128,
Width = 128,
};
Overlay.Visibility = Visibility.Visible;
_currentPopup.Closed += PopupClosing;
_currentPopup.IsOpen = true;
Grid p = new Grid();
p.Background = myBrush;
p.Children.Add(gif);
_currentPopup.Child = p;
}
private void PopupClosing(object sender, EventArgs e)
{
_currentPopup.Closed -= PopupClosing;
_currentPopup = null;
Overlay.Visibility = Visibility.Collapsed;
}
}
}
My Mainwindow.xaml.cs:
namespace uploader
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void sample_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
PopupPanel pop = new PopupPanel();
}
...
I do this in all my WPF applications by using a Canvas with black background and opacity
Example:
<Window>
<Grid>
<!--Main content-->
<UserControl/>
<Grid>
<Canvas Background="Black" Opacity="0.6"/>
<!--Overlay content-->
<UserControl VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</Grid>
</Window>
Using your current code, you will need to handle the visibility of the Canvas overlay.
It's easier to to have it defined within your XAML as shown below:
<Window>
<Grid>
<!--Main content-->
<UserControl/>
<Grid>
<Canvas x:Name="Overlay"
Background="Black"
Opacity="0.6"
Visibility="Collapsed"/>
<!--Overlay content-->
<UserControl VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</Grid>
</Window>
Then, in your code-behind you can set the visibility before the popup opens, and when it closes:
Popup _currentPopup;
private void sample_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
...
_currentPopup = new Popup
{
StaysOpen = false,
AllowsTransparency = true,
VerticalOffset = 350,
HorizontalOffset = 700,
Height = 128,
Width = 128
};
Overlay.Visibility = Visibility.Visible;
_currentPopup.Closed += PopupClosing;
_currentPopup.IsOpen = true;
}
private void PopupClosing(object sender, EventArgs e)
{
_currentPopup.Closed -= PopupClosing;
_currentPopup = null;
Overlay.Visibility = Visibility.Collapsed;
}
Note, that I am using a local variable to keep a reference to the popup. This is so that I can un-subscribe from the Closing event (helps prevent memory leaks)

Displaying transparent image in a WPF DataGrid

I have been tasked with taking an existing list of transparent .png images (currently housed within an ImageList) and displaying them in a WPF DataGrid based on the ImageID column.
I have set up the DataGridColumn as follows:
_dataTemplateColumn = new DataGridTemplateColumn();
_dataTemplateColumn.Header = "";
FrameworkElementFactory _factory = new FrameworkElementFactory(typeof(Image));
Binding _binding = new Binding("Image");
_binding.Mode = BindingMode.TwoWay;
_factory.SetValue(Image.SourceProperty, _binding);
DataTemplate _cellTemplate = new DataTemplate();
_cellTemplate.VisualTree = _factory;
_dataTemplateColumn.CellTemplate = _cellTemplate;
Style _style = new Style();
_style.Setters.Add(new Setter(BackgroundProperty, Brushes.Transparent));
_dataTemplateColumn.CellStyle = _style;
I then create a Custom Object at runtime which includes the image for me and run the following 2 methods on the Image, the first to resize it and the second to convert it into a Bitmap rather than a BitmapImage (which is the only format I have managed to get it working in WPF with so far):
public static Bitmap ResizeImage(this Bitmap Bitmap, Size size)
{
try
{
Bitmap _bitmap = new Bitmap(size.Width, size.Height);
using (Graphics _graphic = Graphics.FromImage((Image)_bitmap))
{
_graphic.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
_graphic.DrawImage(Bitmap, 0, 0, size.Width, size.Height);
}
_bitmap.MakeTransparent(Color.Magenta);
return _bitmap;
}
catch (Exception ex)
{
throw ex;
}
}
public static Bitmap ToBitmap(this BitmapImage BitmapImage)
{
using (MemoryStream _stream = new MemoryStream())
{
BitmapEncoder _encoder = new BmpBitmapEncoder();
_encoder.Frames.Add(BitmapFrame.Create(BitmapImage));
_encoder.Save(_stream);
System.Drawing.Bitmap _bitmap = new System.Drawing.Bitmap(_stream);
_bitmap.MakeTransparent(Color.Magenta);
return new Bitmap(_bitmap);
}
}
The Image is being displayed in the correct size and position in the DataGrid but the transparency is not preserved from the .png format. If anyone knows a better method for me (perhaps it is more correct to take the Image into a resource file first for example?) or a way to get the transparency working within my current code it would be most appreciated!
The following example gives you an idea of how it may look like:
XAML:
<Window ...>
<Window.Resources>
<DataTemplate x:Key="ImageCellTemplate">
<Image Source="{Binding Image}" Width="100"/>
</DataTemplate>
</Window.Resources>
<Grid>
<DataGrid x:Name="dataGrid" AutoGenerateColumns="False"/>
</Grid>
</Window>
Code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var col = new DataGridTemplateColumn();
col.CellTemplate = (DataTemplate)Resources["ImageCellTemplate"];
dataGrid.Columns.Add(col);
foreach (var file in Directory.EnumerateFiles(#"C:\Users\Public\Pictures\Sample Pictures", "*.jpg"))
{
dataGrid.Items.Add(new DataItem { Image = file });
}
}
}
public class DataItem
{
public string Image { get; set; }
}

DrawingVisual not showing in WPF Canvas inside a Window

I created a minimal project in order to "get going" with drawing with DrawingVisuals in WPF (being very beginner so far).
My project contains only XAML and code behind for the main window. The only purpose of this project is open a window and display some "signal noise" filling the available space.
MainWindow.xaml is this:
<Window x:Class="MinimalPlotter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MinimalPlotter"
Title="MainWindow" Width="1200" Height="800"
WindowStartupLocation="CenterScreen">
<Canvas Height="500" Width="700" x:Name="drawingArea" Margin="50" Background="Gray">
<local:VisualHost Canvas.Top="0" Canvas.Left="0" x:Name="host" Width="700" Height="500" />
</Canvas>
</Window>
And code behind is this:
namespace MinimalPlotter
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class VisualHost : FrameworkElement
{
public VisualHost()
{
int h = (int)this.Height;
int w = (int)this.Width;
Random random = new Random();
double r;
DrawingVisual path = new DrawingVisual();
StreamGeometry g = new StreamGeometry();
StreamGeometryContext cr = g.Open();
cr.BeginFigure(new Point(0,0), false, false);
for (int i = 0; i < w; i++)
{
// ugly calculations below to get the signal centered in container
r = (random.NextDouble()-0.5) * h -h/2;
cr.LineTo(new Point(i, r), true, false);
}
cr.Close();
g.Freeze();
DrawingContext crx = path.RenderOpen();
Pen p = new Pen(Brushes.Black, 2);
crx.DrawGeometry(null, p, g);
// Ellipse included for "visual debugging"
crx.DrawEllipse(Brushes.Red, p, new Point(50,50), 45, 20);
crx.Close();
this.AddVisualChild(path);
}
}
}
The problem is: when the window opens, the Canvas shows up in the center, as expected (with a gray background), but no signal gets plotted. A previous version of this code worked fine using Path geometry, but with DrawingVisual no geometry is shown (not even the ellipse geometry included for debugging).
Thanks for reading!
Your VisualHost class would also have to override the VisualChildrenCount property and the GetVisualChild method:
public class VisualHost : FrameworkElement
{
private DrawingVisual path = new DrawingVisual();
public VisualHost()
{
...
AddVisualChild(path);
}
protected override int VisualChildrenCount
{
get { return 1; }
}
protected override Visual GetVisualChild(int index)
{
return path;
}
}
Please note also that it is considered good practice to use IDisposable objects like StreamGeometryContext and DrawingContext in using statements:
var g = new StreamGeometry();
using (var cr = g.Open())
{
cr.BeginFigure(new Point(0,0), false, false);
...
// no need for cr.Close()
}
using (var crx = path.RenderOpen())
{
var p = new Pen(Brushes.Black, 2);
crx.DrawGeometry(null, p, g);
crx.DrawEllipse(Brushes.Red, p, new Point(50,50), 45, 20);
// no need for crx.Close()
}

WPF. Easiest way to move Image to (X,Y) programmatically?

Does anyone know of an easy way to animate a movement from an Image's current location to a new location (X,Y) using WPF animation with no XAML, 100% programmatically? And with no reference to "this" (with RegisterName etc).
I am trying to make an extension class for Image to do animation stuff on it. It is easy enough to change the width and height properties through animation, but after searching for location animation of an object it suddenly becomes more advanced.
As it is an extension class I will only have a reference to the actual Image object and the X and Y I want to move it to.
public static void MoveTo(this Image targetControl, double X, double Y, double Width, double Height){
//code here
...
}
Update:
Thanks. Almost working. It seems The GetTop and GetLeft returns 'NaN' not explicitly set. Found the workaround in this post: Canvas.GetTop() returning NaN
public static void MoveTo(this Image target, double newX, double newY) {
Vector offset = VisualTreeHelper.GetOffset(target);
var top = offset.Y;
var left = offset.X;
TranslateTransform trans = new TranslateTransform();
target.RenderTransform = trans;
DoubleAnimation anim1 = new DoubleAnimation(0, newY - top, TimeSpan.FromSeconds(10));
DoubleAnimation anim2 = new DoubleAnimation(0, newX - left, TimeSpan.FromSeconds(10));
trans.BeginAnimation(TranslateTransform.YProperty, anim1);
trans.BeginAnimation(TranslateTransform.XProperty, anim2);
}
I had to swap two of the values (FROM) with 0. I assume that must be because in this context the upper left corner of the picture is the origin? But now it works.
Try this:
public static void MoveTo(this Image target, double newX, double newY)
{
var top = Canvas.GetTop(target);
var left = Canvas.GetLeft(target);
TranslateTransform trans = new TranslateTransform();
target.RenderTransform = trans;
DoubleAnimation anim1 = new DoubleAnimation(top, newY - top, TimeSpan.FromSeconds(10));
DoubleAnimation anim2 = new DoubleAnimation(left, newX - left, TimeSpan.FromSeconds(10));
trans.BeginAnimation(TranslateTransform.XProperty,anim1);
trans.BeginAnimation(TranslateTransform.YProperty,anim2);
}
Here it is... It changes the size and moves a MediaElement under the Canvas. Just input your parameters:
Storyboard story = new Storyboard();
DoubleAnimation dbWidth = new DoubleAnimation();
dbWidth.From = mediaElement1.Width;
dbWidth.To = 600;
dbWidth.Duration = new Duration(TimeSpan.FromSeconds(.25));
DoubleAnimation dbHeight = new DoubleAnimation();
dbHeight.From = mediaElement1.Height;
dbHeight.To = 400;
dbHeight.Duration = dbWidth.Duration;
story.Children.Add(dbWidth);
Storyboard.SetTargetName(dbWidth, mediaElement1.Name);
Storyboard.SetTargetProperty(dbWidth, new PropertyPath(MediaElement.WidthProperty));
story.Children.Add(dbHeight);
Storyboard.SetTargetName(dbHeight, mediaElement1.Name);
Storyboard.SetTargetProperty(dbHeight, new PropertyPath(MediaElement.HeightProperty));
DoubleAnimation dbCanvasX = new DoubleAnimation();
dbCanvasX.From = 0;
dbCanvasX.To = 5;
dbCanvasX.Duration = new Duration(TimeSpan.FromSeconds(.25));
DoubleAnimation dbCanvasY = new DoubleAnimation();
dbCanvasY.From = 0;
dbCanvasY.To = 5;
dbCanvasY.Duration = dbCanvasX.Duration;
story.Children.Add(dbCanvasX);
Storyboard.SetTargetName(dbCanvasX, mediaElement1.Name);
Storyboard.SetTargetProperty(dbCanvasX, new PropertyPath(Canvas.LeftProperty));
story.Children.Add(dbCanvasY);
Storyboard.SetTargetName(dbCanvasY, mediaElement1.Name);
Storyboard.SetTargetProperty(dbCanvasY, new PropertyPath(Canvas.TopProperty));
story.Begin(this);
<Viewbox Stretch="Uniform" StretchDirection="Both" SnapsToDevicePixels="True">
<Grid Width="640" Height="480" Name="MainLayout" SnapsToDevicePixels="True" Background="Black">
<Canvas Width="640" Height="480" Name="MainCanvas" SnapsToDevicePixels="True">
<MediaElement Height="171" HorizontalAlignment="Left" Name="mediaElement1" VerticalAlignment="Top" Width="337" LoadedBehavior="Manual" Margin="166,140,0,0" Canvas.Left="-162" Canvas.Top="-140" />
<Button Canvas.Left="294" Canvas.Top="196" Content="Button" Height="23" Name="button1" Width="75" Click="button1_Click" />
</Canvas>
</Grid>
</Viewbox>
UPDATE:
Instead of MediaElement use this line:
<Rectangle Height="171" HorizontalAlignment="Left" Name="mediaElement1" VerticalAlignment="Top" Width="337" Margin="166,140,0,0" Canvas.Left="-162" Canvas.Top="-140" Fill="{DynamicResource {x:Static SystemColors.MenuBarBrushKey}}" />
And don't forget to put the C# code to:
private void button1_Click(object sender, RoutedEventArgs e) {}
You can use MediaElement as well but you have to define a VideoClip to see something ;)
Please find a solution that uses the Left and Top properties of Canvas for the extension method. See the following code:
public static void MoveTo(this Image target, Point newP)
{
Point oldP = new Point();
oldP.X = Canvas.GetLeft(target);
oldP.Y = Canvas.GetTop(target);
DoubleAnimation anim1 = new DoubleAnimation(oldP.X, newP.X, TimeSpan.FromSeconds(0.2));
DoubleAnimation anim2 = new DoubleAnimation(oldP.Y, newP.Y , TimeSpan.FromSeconds(0.2));
target.BeginAnimation(Canvas.LeftProperty , anim1);
target.BeginAnimation(Canvas.TopProperty, anim2);
}
This code is based on #DeanChalk's answer.
It moves an Image contained within a Canvas (RFID_Token) diagonally from the top-right to the bottom-left, positioned centrally over another Image within a Canvas (RFID_Reader).
<Canvas>
<Canvas x:Name="RFID_Reader_Canvas">
<Image x:Name="RFID_Reader" Source="RFID-Reader.png" Height="456" Width="682" Canvas.Left="37" Canvas.Top="524"/>
</Canvas>
<Canvas x:Name="RFID_Token_Canvas">
<Image x:Name="RFID_Token" Source="RFID-Token.png" Height="268" Width="343" Canvas.Left="874" Canvas.Top="70"/>
</Canvas>
</Canvas>
var StartX = Canvas.GetLeft(RFID_Token);
var StartY = Canvas.GetTop(RFID_Token);
var EndX = RFID_Reader.Width / 2 + Canvas.GetLeft(RFID_Reader) - StartX - (RFID_Token.Width / 2);
var EndY = RFID_Reader.Height / 2 + Canvas.GetTop(RFID_Reader) - StartY - (RFID_Token.Height / 2);
var AnimationX = new DoubleAnimation(0, EndX, TimeSpan.FromSeconds(1));
var AnimationY = new DoubleAnimation(0, EndY, TimeSpan.FromSeconds(1));
var Transform = new TranslateTransform();
RFID_Token_Canvas.RenderTransform = Transform;
Transform.BeginAnimation(TranslateTransform.XProperty, AnimationX);
Transform.BeginAnimation(TranslateTransform.YProperty, AnimationY);
I kept having NaN or 0 values for my nested elements, here's a modified version of Danny's answer :
public void MoveTo(Canvas canvas, FrameworkElement target, FrameworkElement destination)
{
Point oldPoint = target.TransformToAncestor(canvas).Transform(new Point(0, 0));
Point newPoint = destination.TransformToAncestor(canvas).Transform(new Point(0, 0));
var EndX = destination.Width / 2 + newPoint.X - oldPoint.X - (target.Width / 2);
var EndY = destination.Height / 2 + newPoint.Y - oldPoint.Y - (target.Height / 2);
TranslateTransform trans = new TranslateTransform();
target.RenderTransform = trans;
DoubleAnimation anim1 = new DoubleAnimation(0, EndX, TimeSpan.FromSeconds(0.3));
DoubleAnimation anim2 = new DoubleAnimation(0, EndY, TimeSpan.FromSeconds(0.3));
trans.BeginAnimation(TranslateTransform.XProperty, anim1);
trans.BeginAnimation(TranslateTransform.YProperty, anim2);
}

Resources