How to reuse vector images in wpf properly - c#

X problem:
I want to use vector graphic in WPF.
I have bunch of SVG files, which I can convert into XAML by using Inkscape. Resulting xaml is ResourceDictionary with ViewBox / Canvas containing Path, etc. Dictionaries are merged into App.xaml and I can use key to access them.
Question: how to use such images? It looks like I am not using them properly.
Here is how I am using them
<Viewbox Child="{StaticResource MyImageResourceKey}" Width="100" Height="100"/>
But it looks like I can use it only once (in one place)! Attempting to use that image in multiple places simultaneously will either remove it from previous place or will throw
System.ArgumentException: Must disconnect specified child from current parent Visual before attaching to new parent Visual.
Y problem
I want to show a list of vector images. I display them like this
<ItemsControl ItemsSource="{Binding Images}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Viewbox Width="100" Height="100">
<ContentPresenter Content="{Binding Image}"/>
</Viewbox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
ViewModel
public class ViewModelSomeWindow : INotifyPropertyChanged
{
public class MyImage
{
public object Image { get; set; }
}
private ObservableCollection<MyImage> _images;
public ObservableCollection<MyImage> Images
{
get { return _images; }
set { _images = value; OnPropertyChanged(); }
}
...
}
And items are added like this
Images.Add(new MyImage() { Image = App.Current.Resources["MyImageResourceKey"] });
Problem: when using same image ("MyImageResourceKey") for second item, then first item displays (blank) image. If image is already displayed with the use of StaticResource somewhere, then adding item will throw above ArgumentException.
P.S.: I need to solve Y problem, but perhaps I am not using vector graphic properly.

Please Use x:shared =false in path as shown in attached image
and visit this link too for reference

Related

dynamic selecting rectangles over usercontrols

Let me state problem first. I would like to implement wrapper around Canvas (let me call it Page) which would implement selecting rectangle around its UIElements which are actually selected.
For this I implemented ISelect interface like so :
interface ISelect {
Point Center {get; set;} //Center of selecting rectangle
Size Dimensions {get; set;} //Dimensions of selecting rectangle
}
Every object that is put to Page implements ISelect interface.
Page has SelectedElements of type ObservableCollection which holds reference to all currently selected elements.
For every entry in SelectedElements i would like to draw rectangle around it.
I have few ideas how to do this :
Every UIElement can implement on its own this rectangle and show it when selected. This option would require for new objects to implement this every time. So I rather not use it.
In Page I could create rectangles in code-behind in add them to the Page. It isn't MVVM recommended priniciple.
In Page XAML create somehind like ItemsControl and bind it to SelectedElements with specific template. This option seems like the best one to me. Please help me in this direction. Should I somehow use ItemsControl?
Thank you.
I don't have time to dig a complete working solution, so this is mostly a collection of suggestions.
Each element should have view model
public abstract class Element: INotifyPropertyChanged
{
bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
OnPropertyChanged();
}
}
}
public class EllipseElement : Element {}
public class RectangleElement : Element {}
Then there are data templates to visualize elements (I can't give you converter code, but you can replace it with another, look here).
<DataTemplate DataType="{x:Type local:EllipseElement}">
<Border Visibility="{Binding IsSelected, Converter={local:FalseToHiddenConverter}}">
<Ellipse ... />
</Border>
</DataTemplate>
<DataTemplate DataType="{x:Type local:RectangleElement}">
<Border Visibility="{Binding IsSelected, Converter={local:FalseToHiddenConverter}}">
<Rectangle ... />
</Border>
</DataTemplate>
Then bind ObservableCollection of elements to canvas (which is tricky, see this answer, where ItemsControl is used to support binding).
Your selection routine has to hit-test elements and set/reset their IsSelected property, which will show border. See here regarding how to draw over-all selection rectangle.

XPS export from WPF - only one image is exported

I want to export the content from WPF app (C#,MVVM) to XPS.
I have a bunch of objects and every object contains an image (type=BitmapFrame), which is displayed using source property:
<Image Source="{Binding BusinessUnit.Picture}" />
All those objects are placed on a control called GraphCanvas. Every picture is loaded and it is properly displayed on a screen.
When using the export code:
using (var package = Package.Open(saveFileDialog.FileName, FileMode.Create))
{
var xpsDocument = new XpsDocument(package);
var writer = XpsDocument.CreateXpsDocumentWriter(xpsDocument);
writer.Write(GraphCanvas.ExportCanvas);
xpsDocument.Close();
}
everything is exported properly except of images. Only one image is exported (and it's available in resources folder when browsing through XPS structure) and this one image is used as a substitution for every other image that should be exported.
What might be a reason that all the other images are not exported?
I've faced similar issue when printing multiple pages, and eventually I had to iterate on each Item in page and do this:
fwElement.Arrange(new Rect(new Size(fwElement.ActualWidth, fwElement.ActualHeight)));
Since you have only single element, this might work for you:
GraphCanvas.ExportCanvas.Measure(GraphCanvas.ExportCanvas.RenderSize);
GraphCanvas.ExportCanvas.Arrange(new Rect(new Size(GraphCanvas.ExportCanvas.ActualWidth, GraphCanvas.ExportCanvas.ActualHeight)));
writer.Write(GraphCanvas.ExportCanvas);
I know this question is old, but I had the same issue in my WPF MVVM (I'm using DevExpress MVVM) app, and my resolution may help others in the future.
I had a page of several images, and when printing the 1st image on the page replaced all the other images! I'm not sure why it did this.
Each of my image UI controls was bound to a byte array, and the images displayed perfectly in the app. I resolved the print issue by converting my byte arrays to a BitmapImage to which each image UI control was bound. All images print correctly now.
My View XAML:
<Image Grid.Row="0" Grid.Column="0" Source="{Binding Bitmap[0]}" />
<Image Grid.Row="0" Grid.Column="1" Source="{Binding Bitmap[1]}" />
<Image Grid.Row="0" Grid.Column="2" Source="{Binding Bitmap[2]}" />
<Image Grid.Row="0" Grid.Column="3" Source="{Binding Bitmap[3]}" />
My ViewModel bound properties:
private List<BitmapImage> bitmap;
public virtual List<BitmapImage> Bitmap
{
get { return bitmap; }
set { bitmap= value; }
}
My code for printing each child view in a TabControl (some tabs contained other UI objects other than images):
public void PrintDocument(TabControl control)
{
PrintDocumentImageableArea area = null;
XpsDocumentWriter dw = PrintQueue.CreateXpsDocumentWriter(ref area);
SerializerWriterCollator c = dw.CreateVisualsCollator();
foreach (var tabitem in ((TabControl)control).Items)
{
UIElement childVisual1 = (UIElement)((TabItem)tabitem).Content;
var outputSize = new Size(area.MediaSizeHeight, area.MediaSizeWidth);
((UIElement)childVisual1).Measure(outputSize);
((UIElement)childVisual1).Arrange(new Rect(outputSize));
((UIElement)childVisual1).UpdateLayout();
if (c != null) c.Write(childVisual1);
}
if (c != null) c.EndBatchWrite();
}
This article helped with WPF printing with XPS

WPF Using Image Control to dynamically view Online images

Situation
I'm trying to use an image control in my program that accesses an online image by binding it to a property.
XAML:
<Image Source="{Binding TheImage}" x:Name="imgPic" HorizontalAlignment="Left" Height="113" Margin="14,89,0,0" VerticalAlignment="Top" Width="120"/>
Specifically, I'm binding to the return value of the searchMembers method (which returns an object) mempic contains a URL, and this mempic will change depending on which member is currently logged in.
View Model:
public Uri TheImage
{
get
{
return new Uri(hillracing.searchMembers(Username).mempic);
}
}
Problem
Neither this code, or a static link to the image actually works.
Here is the test image I'm actually using
wondered if anyone could tell em what I was doing wrong.
thanks.
The WPF Image control is more sophisticated than you imagine... just use the string URI value as the Image.Source directly like this:
<Image Source="http://i.imgur.com/aIf7B0P.jpg" />
Or, if you want to data bind, do this:
<Image Source="{Binding TheImage}" ... />
...
public string TheImage
{
get { return "http://i.imgur.com/aIf7B0P.jpg"; }
}

XAML binding Rectange.Fill SolidColorBrush to Color property

I'm trying to display a TextBox and a Rectangle both show the Color property in another class. I use Caliburn.Micro and MVVM approach (I'm new at this).
My problem is that the TextBox writes the Color.ToString by default, but the Rectangle does not fill with the same Color, in fact the rectangle is invisible all the time. How to fill it?
Extract from XAML View:
<TextBox Grid.Row="7" Grid.Column="1" Margin="10,7,20,7"
Text="{Binding Path=Design.EdgeHighlightOutOfRangeColor}"
FontSize="12" FontFamily="Rockwell" Width="110" HorizontalAlignment="Left"/>
<Rectangle Grid.Row="7" Grid.Column="1" Width="20" Height="20" Margin="100,7,20,7">
<Rectangle.Fill>
<SolidColorBrush Color="{Binding Path=Design.EdgeHighlightOutOfRangeColor}"/>
</Rectangle.Fill>
</Rectangle>
Extract from C# ViewModel:
_designSettings = _settings.DesignSettings;
public DesignSettings Design
{
get { return _designSettings; }
set
{
_designSettings = value;
NotifyOfPropertyChange(() => Design);
}
}
Inside the DesignSettings class I have this property to be binded:
public Color EdgeHighlightOutOfRangeColor { get; set; }
Your code should work unless you are using wrong namespace for Color property.
Color can be found under System.Drawing and System.Windows.Media.Colors. Make sure you are using System.Windows.Media in order to bind it with Rectangle.
With System.Drawing, it will come like below; without filled rectangle
Sorry to tell you this, but your code works fine for me without making any changes:
UPDATE >>>
If you resize your Window to make it larger, you should see the Rectangle appear from behind the TextBlock. This is one excellent reason not to use the drag and drop functionality of the Visual Studio designer and Toolbox. If you use rows and columns in your Grid correctly, you won't get this problem.
It should work. The problem why you are not seeing your Rectangle is your are placing TextBox and Rectangle in the same Grid.Row and same Grid.Column of Grid hence Textbox is overlapping on your Rectangle. You will have to place them in different Columns.

Binding image in Isolated Storage

Hey.
I have a list of items that the user can search. The search results are displayed in a listbox. Each animal object has a path to an image in Isolated Storage. What's the quickest way to bind my Image control inside the listboxitem to the image in the isolated storage? Examples I've seen tend to display images from the internet rather than Isolated Storage. If I have around 10 images, it seems to take up all the memory and crash. thanks
EDIT:
I'm using this in my BitmapConverter class (inherits IValueConverter)
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value !=null)
{
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.SetSource(new MemoryStream((Byte[]) value));
return bitmapImage;
}
else
{
return null;
}
}
I have this at the top of my AppResource.xaml file:
<ImageApp_Converter:BitmapConverter x:Key="bmpConverter" />
In my style, within the AppResource.xaml file:
<Image HorizontalAlignment="Left" Margin="8,8,0,4" Width="160" Height="120" Source="{Binding Converter={StaticResource bmpConverter}}" />
I set a breakpoint in my BitmapConverter, but it's never called. I've never used IValueConverter before, so any help would be great. Thanks
There are a few problems in the code shown. Some may just be missing from your example:
Firstly, your binding to the converter does not specify what to bind to to get its value, so it is never called. At a minimum it needs a Path= (or simply a property name as short-cut) or the converter will not be called. Where are you setting the ItemSource of your list?
Secondly, the values getting passed are string file names. Your converter needs to use them as filenames and open a stream based on that name. At the moment it is trying to use the names as byte arrays.
Finally, if the images are a fixed set, it would make more sense to store them in an images folder under ClientBin and simply refer to them with the following path syntax "/images/imagename.jpg" etc. This will involve the browser's caching automatically. You do not need a converter for that. (The key is the leading "/". Without that Silverlight assumes the images are in the current module instead)
Below is a complete example using the images shown in the ClientBin/images folder that looks like this when run:
Sample Xaml file:
<UserControl x:Class="SilverlightApplication1.IsoImages"
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:ImageApp_Converter="clr-namespace:SilverlightApplication1" mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<ListBox x:Name="ImageList">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Background="AliceBlue">
<Image HorizontalAlignment="Left" Margin="8,8,0,4" Width="160" Height="120" Source="{Binding Path=Filename}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
The sample code behind is:
using System.Collections.Generic;
using System.Windows.Controls;
namespace SilverlightApplication1
{
public partial class IsoImages : UserControl
{
public IsoImages()
{
InitializeComponent();
List<ImageItem> images = new List<ImageItem>()
{
new ImageItem("/images/Image1.jpg"),
new ImageItem("/images/Image2.jpg"),
new ImageItem("/images/Image3.jpg"),
new ImageItem("/images/Image4.jpg")
};
this.ImageList.ItemsSource = images;
}
}
public class ImageItem
{
public string Filename{ get; set; }
public ImageItem( string filename )
{
Filename = filename;
}
}
}
You are probably running out of memory because you are repeatedly loading the same file into new BitmapSource objects. You should create only "around 10" BitmapSource objects one for each file. Then re-use those BitmapSource instances by assigning them to Image.Source properties.
One way to do that is to use an implementation of IValueConverter that maintains a static dictionary of file path to BitmapSource key value pairs.

Categories