Binding array of objects using ItemsControl - c#

I need to bind array of object Images to WrapPanel.
I have declared objects in main class constructor:
public MainWindow()
{
InitializeComponent();
private Masina[] _masina = new Masina[12];
DataContext = new
{
data1 = _masina
};
}
My Class Masina haves few variables inside it, but I want to bind just Image:
public class Masina
{
public Image masina_pav = new Image();
public bool r_mas;
public string s_mas;
public Masina()
{
byte[] buffer = File.ReadAllBytes("teksturos/masinos/red/top.png");
MemoryStream memoryStream = new MemoryStream(buffer);
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.DecodePixelWidth = 100;
bitmap.DecodePixelHeight = 200;
bitmap.StreamSource = memoryStream;
bitmap.EndInit();
bitmap.Freeze();
masina_pav.Source = bitmap;
Canvas.SetLeft(masina_pav, 100);
Canvas.SetTop(masina_pav, 200);
}
}
I have tried this XAML code:
<WrapPanel Name="zem" Height="1000" Width="1108" >
<ItemsControl ItemsSource="{Binding data1}" DisplayMemberPath="masina_pav">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Name="masinu_sarasas" HorizontalAlignment="Center" IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</WrapPanel >
For now program starts but don't show me any Image (should be 12 of them). Can someone help me to figure it out ?

Image is a view class that should not be used in view models. Instead, your class should provide a public property of type ImageSource. Note that it's a property, not a field as you had declared it. This is necessary because WPF data binding only works with public properties.
public class Masina
{
public ImageSource MasinaPav { get; private set; }
...
public Masina()
{
using (var fileStream = new FileStream(
"teksturos/masinos/red/top.png",
FileMode.Open, FileAccess.Read, FileShare.Read))
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.DecodePixelWidth = 100;
bitmap.DecodePixelHeight = 200;
bitmap.StreamSource = fileStream;
bitmap.EndInit();
bitmap.Freeze();
MasinaPav = bitmap;
}
}
}
Now your ItemsControl would have an ItemTemplate with an Image control that is bound to the view model property:
<ItemsControl ItemsSource="{Binding data1}">
...
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding MasinaPav}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Besides that, you should be careful with setting a BitmapImage's DecodePixelWidth and DecodePixelHeight at the same time, because it may spoil the bitmap's aspect ratio.

Related

How to load RTF Text from database and display it in data template in a Datagrid? WPF C#

I'm trying to load RTF text from database and display it into datatemplate of custom RichTextBox .
I would like to get the text with style , with code the matter is simple but when I'm trying to use <GridViewDataColumn.CellTemplate> <GridViewDataColumn.CellTemplate/> , it got difficult
Xaml code:
<telerik:GridViewDataColumn DataMemberBinding="{Binding document}" Width="*" x:Name="Description" IsReadOnly="True">
<telerik:GridViewDataColumn.CellTemplate>
<DataTemplate >
<local:UC_Description x:Name="richtext">
</local:UC_Description>
</DataTemplate>
</telerik:GridViewDataColumn.CellTemplate>
C# code:
List= new Controller().GetAll();
foreach (Model item in List)
{
RtfFormatProvider provider = new RtfFormatProvider();
DocumentFormatProvidersManager.RegisterFormatProvider(provider);
byte[] byteArray = Encoding.ASCII.GetBytes(item.Description);
document = provider.Import(byteArray);
FlowDocument flow = new FlowDocument();
}
GridViewList.ItemsSource = null;
GridViewList.ItemsSource = List;
this.DataContext = this;
}
public RadDocument ImportXaml(string content)
{
RtfFormatProvider provider = new RtfFormatProvider();
return provider.Import(text);
}
public string RtfToPlainText(string rtf)
{
byte[] byteArray = Encoding.ASCII.GetBytes(rtf);
var flowDocument = new FlowDocument();
TextRange tr;
using (MemoryStream ms = new MemoryStream(byteArray))
{
tr = new TextRange(flowDocument.ContentStart, flowDocument.ContentEnd);
tr.Load(ms, DataFormats.Rtf);
}
return tr.Text;
}
How can I display text from RTF content in a data template?
A template is a template and there is no UC_Description or RichTextBox until it has been applied at runtime.
What you could do is to handle the Loaded event for the UserControl and then set the Document property of the RichTextBox to your document:
XAML:
<telerik:GridViewDataColumn.CellTemplate>
<DataTemplate >
<local:UC_Description Loaded="OnLoaded" />
</DataTemplate>
</telerik:GridViewDataColumn.CellTemplate>
Code:
private void OnLoaded(object sender, RoutedEventArgs e)
{
UC_Description uc = (UC_Description)sender;
uc.richTextBox.Document = ...;
}

How to force FlowDocument refresh its content on DataContext set?

I'm trying to use FlowDocument with bindings to have separate template which is able to be filled with actual data from data model. Then I convert it to image and print or save on hard-drive.
For binding FlowDocument's Runs with data model I use the code from this article: https://msdn.microsoft.com/en-us/magazine/dd569761.aspx
The FlowDocument template is following:
<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:p="clr-namespace:Labels;assembly=DataModel.Impl"
PageWidth="200" MinPageWidth="200" PageHeight="200" MinPageHeight="200">
<Section>
<Paragraph>
<p:BindableRun BoundText="{Binding Path=Text}"/>
</Paragraph>
</Section>
</FlowDocument>
Code for BindableRun:
public class BindableRun : Run
{
public static readonly DependencyProperty BoundTextProperty = DependencyProperty.Register("BoundText", typeof(string), typeof(BindableRun),
new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure, OnBoundTextChanged, CoerceText));
public BindableRun()
{
FlowDocumentHelpers.FixupDataContext(this);
}
private static void OnBoundTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((Run)d).Text = (string)e.NewValue;
}
private static object CoerceText(DependencyObject d, object value)
{
return value;
}
public String BoundText
{
get { return (string)GetValue(BoundTextProperty); }
set { SetValue(BoundTextProperty, value); }
}
}
Then I load template and set DataContext in it:
private class DataClass
{
public string Text { get; set; }
}
private static FlowDocument LoadFlowDocument(string path)
{
using (var xamlFile = new FileStream(path, FileMode.Open, FileAccess.Read))
{
return XamlReader.Load(xamlFile) as FlowDocument;
}
}
private static void FlowDoc2Image(FlowDocument document, DataClass dataContext, Stream imageStream)
{
var flowDocumentScrollViewer = new FlowDocumentScrollViewer
{
VerticalScrollBarVisibility = ScrollBarVisibility.Hidden,
HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden,
DataContext = dataContext
};
flowDocumentScrollViewer.Document = document;
flowDocumentScrollViewer.Measure(new Size(999999999, 999999999));
//1st pass
flowDocumentScrollViewer.Arrange(new Rect(0, 0, flowDocumentScrollViewer.ActualWidth, flowDocumentScrollViewer.ActualHeight));
//2nd pass. It's not code duplication! Do not remove!
flowDocumentScrollViewer.Arrange(new Rect(0, 0, flowDocumentScrollViewer.ActualWidth, flowDocumentScrollViewer.ActualHeight));
var bitmapRenderer =
new RenderTargetBitmap((int)flowDocumentScrollViewer.ActualWidth, (int)flowDocumentScrollViewer.ActualHeight, 96, 96, PixelFormats.Pbgra32);
bitmapRenderer.Render(flowDocumentScrollViewer);
var pngEncoder = new PngBitmapEncoder { Interlace = PngInterlaceOption.On };
pngEncoder.Frames.Add(BitmapFrame.Create(bitmapRenderer));
pngEncoder.Save(imageStream);
}
public void Test()
{
var doc = LoadFlowDocument("C:\\Experiments\\DocWithBinding.xaml");
var context = new DataClass {Text = "SomeText"};
doc.DataContext = context;
using (var imageStream = new FileStream("C:\\Experiments\\image.png", FileMode.OpenOrCreate, FileAccess.Write))
{
FlowDoc2Image(doc, context, imageStream);
}
}
But nothing happens. I tried to set break points in BindableRun on changing it's value. And I never get there. Changing DataContext doesn't affect the document.
There is no need anymore for the BindableRun class. From the Remarks section in Run.Text:
Starting in the .NET Framework 4, the Text property of the Run object
is a dependency property, which means that you can bind the Text
property to a data source.
So your FlowDocument file could look like this:
<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
PageWidth="200" MinPageWidth="200" PageHeight="200" MinPageHeight="200">
<Section>
<Paragraph>
<Run Text="{Binding Text}"/>
</Paragraph>
</Section>
</FlowDocument>
I've loaded this like shown in your question, assigned a DataClass instance to its DataContext and sucessfully displayed it in a RichTextBox:
<Grid>
<RichTextBox x:Name="rtb"/>
</Grid>
Code behind:
private class DataClass
{
public string Text { get; set; }
}
public MainWindow()
{
InitializeComponent();
var doc = LoadFlowDocument("DocWithBinding.xaml");
doc.DataContext = new DataClass { Text = "Hello, World." };
rtb.Document = doc;
}
private static FlowDocument LoadFlowDocument(string path)
{
using (var xamlFile = new FileStream(path, FileMode.Open, FileAccess.Read))
{
return XamlReader.Load(xamlFile) as FlowDocument;
}
}
EDIT Although you are able to successfully put the FlowDocument into a FlowDocumentScrollViewer, it appears that synchronously rendering this viewer into a RenderTargetBitmap does not create the desired output. It feels like the binding is not yet established, as hard-coded text in the document will render synchronously.
I've tried a few things, but I can't seem to get around adding a short delay before rendering to the bitmap. I did this by making the FlowDoc2Image method async and calling await Task.Delay(100). It is a hack, but it creates the PNG.
private async Task FlowDoc2Image(
FlowDocument document, DataClass dataContext, Stream imageStream)
{
var flowDocumentScrollViewer = new FlowDocumentScrollViewer
{
VerticalScrollBarVisibility = ScrollBarVisibility.Hidden,
HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden,
Document = document,
DataContext = dataContext,
};
flowDocumentScrollViewer.Measure(
new Size(double.PositiveInfinity, double.PositiveInfinity));
flowDocumentScrollViewer.Arrange(new Rect(flowDocumentScrollViewer.DesiredSize));
await Task.Delay(100);
var renderTargetBitmap = new RenderTargetBitmap(
(int)flowDocumentScrollViewer.DesiredSize.Width,
(int)flowDocumentScrollViewer.DesiredSize.Height,
96, 96, PixelFormats.Default);
renderTargetBitmap.Render(flowDocumentScrollViewer);
var pngEncoder = new PngBitmapEncoder { Interlace = PngInterlaceOption.On };
pngEncoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
pngEncoder.Save(imageStream);
}

Displaying image from a class attribute in a button

i need to display an image which is from a class attribute inside a button, the binding doesn't work.
This is how i proceed:
i have a class Products that contains ProductImage of type system.drawable.image and some other strings.
XAML code :
<Button Content="{Binding ProductImage}" Name="ImageButton"></Button>
the button is showing with Content : System.Drawing.Bitmap in it.
any help Please.
EDIT : i found important to add the Window2.XAML.cs file
MySqlConnection connection = new MySqlConnection(ConnectionManager.ConnectionString);
try
{
connection.Open();
MySqlCommand command = new MySqlCommand("SELECT * FROM products", connection);
MySqlDataReader reader = command.ExecuteReader();
var list = new List<ProductsBLL>();
while (reader.Read())
{
ProductsBLL product = new ProductsBLL();
product.ProductId = (int) reader[0];
product.ProductName = (string) reader[1];
product.ProductReference = (string) reader[2];
product.ProductColor = (string) reader[3];
var imagems = (byte[]) reader[4];
MemoryStream ms = new MemoryStream(imagems);
product.ProductImage = System.Drawing.Image.FromStream(ms);
product.ProductDescription = (string) reader[5];
product.ProductUnitPrice = (decimal) reader[6];
product.ProductUnitInStock = (int) reader[7];
product.ProductUnitInCommand = (int) reader[8];
list.Add(product);
product = null;
}
ProductsListView.ItemsSource = list;
}
catch (Exception e)
{
MessageBox.Show(this, e.Message);
}
finally
{
if(connection.State == ConnectionState.Open)
connection.Close();
}
The type of your ProductImage property is currently System.Drawing.Image, which is WinForms, not WPF. A System.Drawing.Image can not be drawn directly by a WPF Image control.
You should change the property type to System.Windows.Media.ImageSource:
public ImageSource ProductImage { get; set; }
Now you would create an instance of either System.Windows.Media.Imaging.BitmapImage or
System.Windows.Media.Imaging.BitmapFrame as value of the property. Note that the BitmapCacheOption.OnLoad must be set, because you want to dispose of the stream right after loading the image.
byte[] imageBuffer = ...
using (var memoryStream = new MemoryStream(imageBuffer))
{
product.ProductImage = BitmapFrame.Create(
memoryStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
or
using (var memoryStream = new MemoryStream(imageBuffer))
{
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = memoryStream;
bitmapImage.EndInit();
product.ProductImage = bitmapImage;
}
Finally (as already said in the other answer) you would have to use an Image control as the Button's Content:
<Button Name="ImageButton">
<Image Source="{Binding ProductImage}" />
</Button>
Try to use Image control as place holder for Bitmap :
<Button Name="ImageButton">
<Image Source="{Binding ProductImage}" />
</Button>

How to release Image from Image Source in WPF

Am loading image like below
XAML
<Image Stretch="None" Grid.Row="16" Height="70" HorizontalAlignment="Left" Name="imgThumbnail" VerticalAlignment="Top" Width="70" Grid.RowSpan="3" Margin="133,1,0,0" Grid.Column="2" Grid.ColumnSpan="2" />
CodeBehind
if (Path.GetFileNameWithoutExtension(filePath).ToLower().Contains(slugName.ToLower() + "_70x70"))
{
imgThumbnail.BeginInit();
imgThumbnail.Stretch = Stretch.UniformToFill;
imgThumbnail.Source = new BitmapImage(new Uri(filePath));
imgThumbnail.EndInit();
count = count + 1;
}
Above code work fine , now I have a delete button next to my thumbnail, if delete button called I suppose to delete all the images from the source location.
Here is the code to delete the image files
internal int Remove(string slugName, DirectoryInfo outputFolder)
{
Helper.MetadataView.imgThumbnail.Source = null;
foreach (string filePath_ToBeDeleted in filePathList_ToBeDeleted)
{
if (File.Exists(filePath_ToBeDeleted))
{
Helper.MetadataView.imgThumbnail.IsEnabled = false;
File.Delete(filePath_ToBeDeleted);
count += 1;
}
}
return count;
}
return 0; // slugName == null
}
I tried to source to be null and delete, but it throws exception like below
The process cannot access the file '\serv1\Dev\Images\730_Test4_0406_70x70.jpg' because it is being used by another process.
Am not sure how to dispose, please someone guide me.
You should not use that Image directly in your application if you want to delete or move it.
imgThumbnail.Source = new BitmapImage(new Uri(filePath));
Instead, do this:
BitmapImage image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(filePath);
image.EndInit();
imgThumbnail.Source = image;
For more read this
To have a good code-reuse a binding converter could be used:
namespace Controls
{
[ValueConversion(typeof(String), typeof(ImageSource))]
public class StringToImageSourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is string valueString))
{
return null;
}
try
{
ImageSource image = BitmapFrame.Create(new Uri(valueString), BitmapCreateOptions.IgnoreImageCache, BitmapCacheOption.OnLoad);
return image;
}
catch { return null; }
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
And there is a string for binding, for example
public string MyImageString { get; set; } = #"C:\test.jpg"
And in the UI the converter is used, in my case from the Library named "Controls"
<Window x:Class="MainFrame"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Controls;assembly=Controls">
<Window.Resources>
<controls:StringToImageSourceConverter x:Key="StringToImageSourceConverter" />
</Window.Resources>
<Grid>
<Image Source="{Binding MyImageString, Converter={StaticResource StringToImageSourceConverter}}" />
</Grid>
</Window>

Add images to ListBox (c#, windows phone 7)

I am developing the Windows Phone 7, I need to add images to ListBox.
I want do it with C# code, not XAML.
I read about it, but everyone uses BitmapImage, which I can't get to work on Windows Phone 7.
I have XAML code:
<StackPanel Margin="0,0,0,17">
<local:MatchDates Adress="http://soccernet.espn.go.com/results/_/league/esp.1/spanish-la-liga?cc=57393" />
</StackPanel>
and C# function in my class MatchDates:
void add_items(List<List<string>> code)
{
if (code.Count == 0)
this.Items.Add("no mathes");
else
{
foreach (List<string> temp1 in code)
{
foreach (string temp2 in temp1)
{
this.Items.Add(temp2);
}
this.Items.Add("---------------------------------");
}
}
}
How can I add images in function add_items?
This code:
Uri uri = new Uri("/eng.png", UriKind.Relative);
BitmapImage bitmap = new BitmapImage(uri);
Image img = new Image();
img.Height = 30;
img.Width = 30;
img.Source = bitmap;
this.Items.Add(img);
this.Items.Add(temp2);
Only empty space is presented, How do I add images to ListBox?
One of the ways I used:
XAML:
<ListBox Name="lstView" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ImagePath}"></Image>
<TextBlock Text="{Binding Name}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
C#:
public class Article
{
public string Name { get; set; }
public string ImagePath { get; set; }
}
Article article1 = new Article() { Name = "name1", ImagePath = "path of image 1" };
Article article2 = new Article() { Name = "name2", ImagePath = "path of image 2" };
var articles = new List<Article>();
articles.Add(article1);
articles.Add(article2);
lstView.DataContext = articles;
For retrieving articles I use WCF service.
BitmapImage is supported. You have to declare the BitmapImage as ImageSource.

Categories