how can i show different backgrounds in multiple buttons in WPF - c#

I need to show multiple buttons, but each one must have a different background than other buttons, I have been working on it, but I only got to display multiple buttons but with the same background.
Here is the XAML:
<Window x:Class="apple.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="370" Width="525">
<Grid>
<Image Source="C:\Users\Public\Pictures\Sample Pictures\Koala.jpg" Stretch="Fill"/>
<DockPanel Name="dock">
<UniformGrid Name="gridx" DockPanel.Dock="Top" Rows="3" Columns="3" Height="334">
</UniformGrid>
</DockPanel>
</Grid>
</Window>
Also, here is the C# code:
namespace apple
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
masterGUI();
}
public void masterGUI()
{
ImageBrush ib = new ImageBrush();
IconImage[] ico = null;
Bitmap[] img = null;
string[] list = null;
string[] link = Directory.GetFiles(#"C:\ProgramData\Microsoft\Windows\Start Menu\Programs", "*.lnk", SearchOption.AllDirectories);
list = new string[link.Length];
ico = new Icon[link.Length];
img = new Bitmap[link.Length];
for (int n = 0; n < link.Length; n++)
{
System.Windows.Controls.Button newBtn = new Button();
list[n] = System.IO.Path.GetFileNameWithoutExtension(link[n]);
FileToImageIconConverter some = new FileToImageIconConverter(link[n]);
ImageSource imgSource = some.Icon;
ib.ImageSource = imgSource;
newBtn.Background = ib;
newBtn.Content = list[n];
gridx.Children.Add(newBtn);
}
}
}
}
Any idea? thank you.

The ImageBrush needs to be created in the for-loop individually for each item. Otherwise you will end up with the same background for every item.
Also you are approaching this the "wrong" way, in WPF you should use data binding and data templating for this sort of thing instead of imperative looping.

Related

Saving image (screenshot) of control in WPF - MVVM Pattern

I'm trying to take a screenshot of user control (viewport) in WPF application. My problem is that I don't know how to get the position of window or viewport while staying in accord with MVVM pattern.
Here is the method, but it takes all the screen instead of my viewport, since I don't know how to bind window/viewport properties to it.
private void saveScreenshot()
{
double screenLeft = SystemParameters.VirtualScreenLeft;
double screenTop = SystemParameters.VirtualScreenTop;
double screenWidth = SystemParameters.VirtualScreenWidth;
double screenHeight = SystemParameters.VirtualScreenHeight;
using (Bitmap bmp = new Bitmap((int)screenWidth, (int)screenHeight))
{
using (Graphics g = Graphics.FromImage(bmp))
{
string fileName = "ScreenCapture -" + DateTime.Now.ToString("ddMMyyyy-hhmmss") + ".png";
g.CopyFromScreen((int)screenLeft, (int)screenTop, 0, 0, bmp.Size);
SaveFileDialog saveFile = new SaveFileDialog(); ;
saveFile.Filter = "JPEG|*.jpeg|Bitmap|*.bmp|Gif|*.gif|Png|*.png";
if (saveFile.ShowDialog() == true)
{
bmp.Save(saveFile.FileName);
}
}
}
}
I was trying to bind some window properties to ViewModel, to get access to window position on screen, but it didn't work.
XAML:
<Window
x:Class="SimpleDemo.MainView"
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:hx="http://helix-toolkit.org/wpf/SharpDX"
xmlns:hw="http://helix-toolkit.org/wpf"
xmlns:local="clr-namespace:SimpleDemo"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="Point cloud visualization"
Width="1280"
Height="720"
Left="{Binding LeftScreenPosition, Mode=TwoWay}"
Top="{Binding TopScreenPosition, Mode=TwoWay}"
mc:Ignorable="d" Background="#FF465065" Foreground="#FFFFFEFE">
And here is the ViewModel (SetValue contains PropertyChanged event things)
private double leftScreenPosition;
private double topScreenPosition;
public double LeftScreenPosition
{
get { return leftScreenPosition; }
set { SetValue(ref leftScreenPosition, value); }
}
public double TopScreenPosition
{
get { return leftScreenPosition; }
set { SetValue(ref leftScreenPosition, value); }
}
I would be really grateful if someone could explain to me where is the mistake, or show me another way to deal with it (Please consider that i'm a total beginner, thank you).
Trick with CommandParameter can let you pass some FrameworkElement to your command placed in your ViewModel. My example
xaml (you can refine if you need only your control in CommandParameter)
<Window x:Class="WpfTestApp.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"
xmlns:local="clr-namespace:WpfTestApp"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
d:DataContext="{d:DesignInstance Type=local:YourViewModel, IsDesignTimeCreatable=True}">
<Grid>
<Button Content="Make screenShot" Command="{Binding MakeScreenShotCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}"/>
</Grid>
ViewModel
public class YourViewModel
{
private ICommand _makeScreenShotCommand;
public ICommand MakeScreenShotCommand => _makeScreenShotCommand ?? (_makeScreenShotCommand = new ActionCommand(TakeScreenShot));
private void TakeScreenShot(object control)
{
FrameworkElement element = control as FrameworkElement;
if (element == null)
throw new Exception("Invalid parameter");
RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap((int)element.ActualWidth, (int)element.ActualHeight, 96, 96, PixelFormats.Pbgra32);
renderTargetBitmap.Render(element);
PngBitmapEncoder pngImage = new PngBitmapEncoder();
pngImage.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
using (Stream fileStream = File.Create("filePath"))
{
pngImage.Save(fileStream);
}
}
}

WPF Viewbox and Image size

I want to create the window that would show list of pictures one below the other. I've created control that contains ViewBox and Image in it:
<UserControl x:Class="..."
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Viewbox Name="viewbox">
<Image Height="10" Name="image" Width="10" HorizontalAlignment="Left" VerticalAlignment="Top" />
</Viewbox>
</Grid>
</UserControl>
public BitmapImage Image
{
get { return image.Source as BitmapImage; }
set { changeImage(value); }
}
public SingleIllustrationViewer()
{
InitializeComponent();
}
private void changeImage(BitmapImage img)
{
System.Drawing.Graphics graphics = System.Drawing.Graphics.FromHwnd(IntPtr.Zero);
float dpiX = graphics.DpiX / 96;
this.image.BeginInit();
this.image.Source = img;
this.image.EndInit();
this.image.Width = img.PixelWidth / img.DpiX * dpiX;
}
and I'm placing Images on window like this:
double margin = 0;
for (int i = 0; i < illustrations.Count; i++)
{
String path = illustrations[i].printVersions.Last<String>();
BitmapImage bmp = new BitmapImage(new Uri(path));
Controls.SingleIllustrationViewer iv = new Controls.SingleIllustrationViewer();
iv.VerticalAlignment = System.Windows.VerticalAlignment.Top;
iv.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
iv.Margin = new Thickness(50, margin, 0, 0);
iv.Image = bmp;
grid.Children.Add(iv);
margin += iv.Image.Height + 20;
}
So, for example, I've placed 3 pictures (all 3 of same width) like this, and received such an interesting behavior: first one is good, second smaller, third smaller than a second. Here is the screen shot:
Maybe someone can tell me why is that so, and how can fix this, to see all those picture in the same width?
Thanks!
Regards, Tomas
Root cause:
You did not specify the Height or Width of the UserControl, so when the first SingleIllustrationViewer is added to the Grid, it will be stretched to occupied all available space until it reaches the edge of the Grid. The same happens to the second one, but it is constrained to a smaller region due to the incremented margin.
The size specified as
d:DesignHeight="300" d:DesignWidth="300"
is only used by designer, set the size like
Height="300" Width="300"
And then, put you viewers in a StackPanel instead of a Grid, then you don't have to calculate the Margin of a viewer base on the last viewer's position. StackPanel is a container that stacks its children in one direction, vertically or horizontally.
for (int i = 0; i < illustrations.Count; i++)
{
String path = illustrations[i].printVersions.Last<String>();
BitmapImage bmp = new BitmapImage(new Uri(path));
Controls.SingleIllustrationViewer iv = new Controls.SingleIllustrationViewer();
iv.VerticalAlignment = System.Windows.VerticalAlignment.Top;
iv.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
iv.Margin = new Thickness(50, 0, 0, 20); //50 px to the left, 20 px to the next child
iv.Image = bmp;
stackPanel1.Children.Add(iv);
}

Custom UserControl not showing up in ListBox

I have a problem displaying custom UserControls in my ListBox programmatically. I just can't seem to figure out what is wrong. The list-item shows up without image or text.
My project consists of:
MainWindow.xaml
MainWindow.xaml.cs
cvMenuItem.xaml
cvMenuItem.xaml.cs
Code of MainWindow.xaml.cs
private void cvMenuItem_MouseLeftButtonUp_1(object sender, MouseButtonEventArgs e)
{
lstContacts.Items.Clear();
cvMenuItem test = new cvMenuItem("test",
Environment.GetEnvironmentVariable("USERPROFILE") + #"\Downloads\images.jpg");
lstContacts.Items.Add(test);
}
Code of cvMenuItem.xaml.cs
public partial class cvMenuItem : UserControl
{
public cvMenuItem()
{
InitializeComponent();
}
public cvMenuItem(string text, string Logo)
{
this.Height = 50;
this.Width = 186;
txtService = new TextBlock() { Width = 100, Height = 50 };
imgLogo = new Image() { Width = 50, Height = 50 };
//Just found out, adding the objects as childeren partially works
this.AddChild(imgLogo);
//But I can't add txtService as Childeren
//this.AddChild(txtService);
this.Services = text;
this.Logo = Logo;
}
public string Services
{
get{ return txtService.Text.ToString() }
set
{
txtService.Text = value;
}
}
public string Logo
{
get{ return imgLogo.Source.ToString(); }
set
{
var uriSource = new Uri(value);
imgLogo.Source = new BitmapImage(uriSource);
}
}
My cvMenuItem.xaml.cs
<UserControl x:Class="WpfApplication1.cvMenuItem"
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"
mc:Ignorable="d" Height="50" Width="186">
<Grid Width="186" VerticalAlignment="Top">
<Image Name="imgLogo" Height="50" Width="50" HorizontalAlignment="Left" VerticalAlignment="Top" OpacityMask="{DynamicResource {x:Static SystemColors.ActiveCaptionTextBrushKey}}" />
<TextBlock Name="txtService" HorizontalAlignment="Left" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Bottom" Height="18" Width="121" Margin="70,0,0,18" RenderTransformOrigin="0.499,1.932"/>
</Grid>
</UserControl>
First of all you need to call InitializeComponent in the custom constructor you have added, as that is needed to process the XAML properly. Otherwise all the controls you add in the XAML will be null when running the application.
Additionally it makes no sense to create the TextBlock and Image again in the code-behind. You just have to use the ones created in the XAML.
So to get it working, change the code in the constructor to the following:
public cvMenuItem(string text, string Logo)
{
InitializeComponent();
this.Height = 50;
this.Width = 186;
this.Services = text;
this.Logo = Logo;
}

ListViewItem.Content only updates last item in ListView c# WPF

I have a program which works like a simple power point program. I am not working in a MVVM pattern.
I am saving all the slides that a user has created to an XML file and then reading this back in at a later date. My ListView which stores a preview of all slides currently created in the program when loaded only loads the ListView.Items.Content for the last slide in the list.
The following is the code I use to read the XML file.
private void Load()
{
List<SlideItems> listProjectContents = DeSerializeObjects();
if (listProjectContents != null)
{
int loadCount = 0;
foreach (SlideItems slide in listProjectContents)
{
CreateNewSlide();
ListViewItem i = (ListViewItem)slideListView.Items[loadCount++];
BitmapSource bSource = Base64ToImage(slide.slidePreview);
Image img = new Image();
img.Source = bSource;
img.Height = 90;
img.Width = 190;
Border b = new Border();
StackPanel s = new StackPanel();
TextBlock t = new TextBlock();
t.Inlines.Add(loadCount.ToString());
s.Children.Add(t);
s.Children.Add(img);
b.Child = s;
i.Content = b;
}
}
}
Can anyone see from this why only the ListView.Items.Content of the last ListView Item is displayed when I load from an XML file??
This code is awful. Instead of trying to fix it, you should try to understand how things are done in wpf. Even if for some reason you are not using MVVM you can (and should) use data binding. In your case, you should
1) define a data template:
<ListView x:Name="slideListView">
<ListView.ItemTemplate>
<DataTemplate>
<Border>
<StackPanel>
<TextBlock Text="{Binding Text}"/>
<Image Width="190" Height="90" Source="{Binding Image}"/>
</StackPanel>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
2) define item
class MyItem
{
public BitmapSource Image { get; set; }
public string Text { get; set; }
}
3) populate your ListView (should be done using MVVM instead, but oh well)
private void Load()
{
slideListView.Items.Clear();
List<SlideItems> listProjectContents = DeSerializeObjects();
if (listProjectContents != null)
{
int loadCount = 0;
foreach (SlideItems slide in listProjectContents)
{
BitmapSource bSource = Base64ToImage(slide.slidePreview);
var item = new MyItem { Text = (loadCount++).ToString(), Image = bSource };
slideListView.Items.Add(item);
}
}
}

Creating WriteableBitmap form chart issue

I have a problem when trying to create writeable bitmap form Silverlight toolkit Graph.
When using textBlock, everything is fine, but after trying to use Chart, generated bitmap is empty :( .
var data = new List<Point>(100);
for (int i = 0; i < 100; i++)
{
data.Add(new Point(i, Math.Sin(i * Math.PI / 50)));
}
Chart chart_ = new Chart()
{
Name = "Chart",
Width = 512,
Height = 512
};
LineSeries line = new LineSeries()
{
Name = "Line",
Title = "test",
IndependentValuePath = "X",
DependentValuePath = "Y",
ItemsSource = data
};
chart_.Series.Add(line);
This code creates chart with sinusoid in it. Then Im trying to create bitmap from it.
LayoutRoot.Children.Add(chart_); // I tried to add chart_ to visual tree, It doesn't help
//creates bitmap
ScaleTransform t = new ScaleTransform() { ScaleX = 1.0, ScaleY = 1.0 };
//bitmap = new WriteableBitmap(chart_, t); Tried it also with this way
bitmap = new WriteableBitmap(512, 512);
bitmap.Render(chart_, t);
texture = new Texture2D(GraphicsDeviceManager.Current.GraphicsDevice, bitmap.PixelWidth, bitmap.PixelHeight, false, SurfaceFormat.Color);
bitmap.CopyTo(texture);
All this code creates Empty Bitmap.But when I use TextBlock or some primitives like Ellipse, everything works. Im sure, that code generating chart is fine, cause chart is generated fine in Silverlight control.
EDIT:
I tried to create bitmap this way, but it dont help.
chart_.InvalidateMeasure();
bitmap = new WriteableBitmap(512, 512);
bitmap.Render(chart_, null);
bitmap.Invalidate();
EDIT 2:
I don't want graph to be in visual tree. I just need generate image of it an than use it in XNA part of my application.
Just a couple of small changes, but I suspect the big change is manually adding a reference to System.Windows.Controls after using NuGet to add the Charting package.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Controls.DataVisualization.Charting;
using System.Windows.Media.Imaging;
namespace SilverlightApplication1
{
public partial class MainPage : UserControl
{
Chart chart_;
public MainPage()
{
InitializeComponent();
var data = new List<Point>(100);
for (int i = 0; i < 100; i++)
{
data.Add(new Point(i, Math.Sin(i * Math.PI / 50)));
}
chart_ = new Chart()
{
Name = "Chart",
Width = 512,
Height = 512
};
LineSeries line = new LineSeries()
{
Name = "Line",
Title = "test",
IndependentValuePath = "X",
DependentValuePath = "Y",
ItemsSource = data
};
chart_.Series.Add(line);
LayoutRoot.Children.Add(chart_); // I tried to add chart_ to visual tree, It doesn't help
}
private void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
{
//creates bitmap
WriteableBitmap bitmap;
ScaleTransform t = new ScaleTransform() { ScaleX = 1.0, ScaleY = 1.0 };
//bitmap = new WriteableBitmap(chart_, t); Tried it also with this way
bitmap = new WriteableBitmap(512, 512);
bitmap.Render(chart_, t);
//texture = new Texture2D(GraphicsDeviceManager.Current.GraphicsDevice, bitmap.PixelWidth, bitmap.PixelHeight, false, SurfaceFormat.Color);
//bitmap.CopyTo(texture);
image1.Source = bitmap;
}
}
}
And
<UserControl x:Class="SilverlightApplication1.MainPage"
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"
d:DesignHeight="300" d:DesignWidth="400"
xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit">
<Grid x:Name="LayoutRoot" Background="White" Loaded="LayoutRoot_Loaded">
<Image Height="150" HorizontalAlignment="Left" Margin="97,109,0,0" Name="image1" Stretch="Fill" VerticalAlignment="Top" Width="200" />
</Grid>
</UserControl>
That should get you going...
Don't forget to delete all of the attempted workarounds - I don't think any of them are necessary. The only code I did not use from your example had to do with the textures - I didn't know what to do with that and it wasn't the problem you were having anyways...
David
I believe that by the time you are creating WriteableBitmap object & calling Render method, Chart has not be rendered yet. You can check this by moving these lines of code to some other event like Button_Click etc. Have a look at below codes as it is creating the WriteableBitmap and then passes it to image control as source....
XAML Code.....
<UserControl x:Class="TryBitmap.MainPage"
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"
d:DesignHeight="300" d:DesignWidth="400"
xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="35"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Grid.Row="0" Height="25" Width="100" Content="Capture" Click="Button_Click"/>
<Grid x:Name="grdGraphs" Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image x:Name="img" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Grid>
Code Behind....
public partial class MainPage : UserControl
{
private Chart chart_;
public MainPage()
{
InitializeComponent();
Loaded += new RoutedEventHandler(MainPage_Loaded);
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
var data = new List<Point>(100);
for (int i = 0; i < 100; i++)
{
data.Add(new Point(i, Math.Sin(i * Math.PI / 50)));
}
chart_ = new Chart()
{
Name = "Chart",
Width = 512,
Height = 512
};
LineSeries line = new LineSeries()
{
Name = "Line",
Title = "test",
IndependentValuePath = "X",
DependentValuePath = "Y",
ItemsSource = data
};
chart_.Series.Add(line);
chart_.SetValue(Grid.ColumnProperty, 0);
grdGraphs.Children.Add(chart_);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var bitmap = new WriteableBitmap((int)(chart_.RenderSize.Width), (int)(chart_.RenderSize.Height));
bitmap.Render(chart_, new MatrixTransform());
bitmap.Invalidate();
img.Source = bitmap;
}
}
I'm afraid this is only going to be half an answer. I can solve your immediate problem but I'm afraid you'll just end up with another one I haven't managed to solve.
Creating the bitmap after a call to Dispatcher.BeginInvoke should ensure that your bitmap isn't completely blank, i.e.:
// do stuff with chart_ ...
Dispatcher.BeginInvoke(() =>
{
bitmap = new WriteableBitmap(512, 512);
bitmap.Render(chart_, null);
bitmap.Invalidate();
// At this point, the bitmap shouldn't be blank.
});
However, the results of this likely to be less than satisfactory. I ran your code and I found that the LineSeries was missing from the chart image, although the rest of the chart was there. This remained true even after I set all Durations of the various animations in the ControlTemplate for the LineSeries to 0 and additionally set the following properties on the chart:
chart_.AnimationSequence = AnimationSequence.Simultaneous;
chart_.TransitionDuration = new TimeSpan(0);
I tried wrapping the WriteableBitmap operations in further calls to Dispatcher.BeginInvoke(), and after doing this the data points started to appear more clearly. However, I can't believe that an approach like this is the right solution to the problem.

Categories

Resources