C# Add buttons with images and handlers - c#

I have a simple problem, but since it's the first time I'm using C#/XAML here I go.
The XAML file contains a StackPanel to which I want to add clickable images at runtime.
<StackPanel x:Name="grid_" Orientation="Horizontal" Margin="10 0 10 0" VerticalAlignment="Center">
</StackPanel>
I didn't succeed in creating a click event on those images, and I've read I could try with buttons, changing their background. This is my code:
for (int i = 0; i < 20; i++)
{
Button j = new Button();
//Image j = new Image();
//j.Source = new BitmapImage(new Uri("Images/my_thumb.png", UriKind.Relative));
var brush = new ImageBrush();
j.BackgroundImage = brush;
//j.MouseDown += new RoutedEventHandler(this.changeImage);
grid_.Children.Add(j);
Grid.SetRow(j, i);
}
Now, how can I change the button image, and add a handler to retrieve which button has been clicked? The error I'm getting is
Error 3 'System.Windows.Controls.Button' does not contain a definition for 'BackgroundImage' and no extension method 'BackgroundImage' accepting a first argument of type 'System.Windows.Controls.Button' could be found (are you missing a using directive or an assembly reference?)
You can see from my comments in the code that I am still retaining the plain image approach, just to be sure: in case you know how to make my images clickable, please tell me :)
I can settle down for an event handler that could just retrieve the button image source file name, in case.
Can you point me in the right direction? Remember I am a complete newbie! :)
Thanks!

You could use the Click event
j.Click += new EventHandler(onButtonClick);
And then in the event handler
void onButtonClick(Object sender, EventArgs e)
{
var clickedButton = sender as Button;
// do your stuff..
}

XAML
<Button x:Name="button" Content="Button1" HorizontalAlignment="Left" Margin="400,20,0,0" VerticalAlignment="Top" RenderTransformOrigin="-1.258,-5" Click="Button_Click" Height="80" Width="80"/>
C#
private void Button1_Click(object sender, RoutedEventArgs e)
{
button1.Background = new ImageBrush { ImageSource = new BitmapImage(new Uri("ms-appx:/Images/timerg.png", UriKind.RelativeOrAbsolute)) };
}
or C#
private void Button1_Click(object sender, RoutedEventArgs e)
{
BitmapImage bmp = new BitmapImage();
Uri u = new Uri("ms-appx:/Images/timer.png", UriKind.RelativeOrAbsolute);
bmp.UriSource = u;
// NOTE: change starts here
Image i = new Image();
i.Source = bmp;
button1.Content = i;
}

Related

Drag & Drop from explorer to wpf element

Everything seems to be simple and there are quite a few tutorials, but I cannot transfer data (in my case, an image) to a wpf window element. I was able to implement the transfer of an image from one element to another. But when I capture an image (for example, a desktop), when I transfer it to the desired element, the transfer option does not even appear, only a crossed-out circle and does not work out more than one event associated with drop (as if AllowDrop = false)
My code:
XAML
<Image x:Name="mainContent" Grid.Column="1" Stretch="Fill" AllowDrop="True" Drop="MainContent_Drop" />
C#
private void SpImageLeft_MouseDown(object sender, MouseButtonEventArgs e)
{
Image image = sender as Image;
DragDrop.DoDragDrop(image, image, DragDropEffects.Copy);
}
private void MainContent_Drop(object sender, DragEventArgs e)
{
Image image = (Image)e.Data.GetData(typeof(Image));
mainContent.Source = image.Source;
}
I understand that when I take an image from explorer it will be different there, something like this, but it still does not even show that you can add an image
private void MainContent_Drop(object sender, DragEventArgs e)
{
string[] arr = (string[])e.Data.GetData(DataFormats.FileDrop);
mainContent.Source = (ImageSource)new ImageSourceConverter().ConvertFromString(arr[0]);
}
The following worked for me as a Drop event handler for an Image control:
private void OnMainImageDrop(object sender, DragEventArgs e)
{
if (sender is Image image && e.Data.GetDataPresent(DataFormats.FileDrop))
{
if (e.Data.GetData(DataFormats.FileDrop) is string[] filePaths)
{
image.Source.Freeze();
string filePath = filePaths[0];
var uriSource = new Uri(filePath);
var imageSource = new BitmapImage(uriSource);
image.Source = imageSource;
}
}
}
I used a placeholder image to make sure the image had a size and served as a mouse hover surface.
XAML:
<Image x:Name="MainImage" Grid.Row="1"
Source="Images/DotNetLogo.png"
Stretch="Uniform"
AllowDrop="True" Drop="OnMainImageDrop"/>

WPF MouseEnter/MouseLeave/MouseMove Event Tunneling

I am trying to make a button where the contents change when the mouse enters the button.
Currently, this is the code that I'm working with:
Xaml
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Button x:Name="One"
Content="ONE"
Height="50"
Width="Auto"
MouseEnter="One_OnMouseEnter"
MouseLeave="One_OnMouseLeave" />
<Button x:Name="Two"
PreviewMouseMove="Two_OnMouseEnter">
<Grid>
<Ellipse Fill="Black"
Height="40"
Width="40" />
<Label Content="TWO"
Foreground="White"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</Grid>
</Button>
</StackPanel>
</StackPanel>
C# Code-Behind File
private void One_OnMouseEnter(object sender, MouseEventArgs e)
{
Button b = sender as Button;
if (b != null)
{
b.Foreground = Brushes.Purple;
b.FontSize = 24;
}
}
private void One_OnMouseLeave(object sender, MouseEventArgs e)
{
Button b = sender as Button;
if (b != null)
{
b.Foreground = Brushes.Black;
b.FontSize = 12;
}
}
private void Two_OnMouseEnter(object sender, MouseEventArgs e)
{
Ellipse el = sender as Ellipse;
if (el != null)
{
el.Height = 60;
el.Width = 60;
el.Fill = Brushes.White;
}
Label l = sender as Label;
if (l != null)
{
l.Foreground = Brushes.Black;
}
Grid g = sender as Grid;
if (g != null)
{
g.Height = 200;
g.Width = 200;
}
}
The first button that's there works as expected.
When the mouse moves onto the "One" button, the text contents changes as expected. Text color changes to purple, and font size increases.
I am trying to do something similar with the second button. Increase the size and change the color of the elliptical, change the color of the label, and change the size of the grid.
The problem is that the second button does not seem to respond as expected. I've tried to use PreviewMouseMove, which I understand to use a Tunneling routing strategy, which should trigger on the Button's child elements. I have used breakpoints to check, and the event seems to only trigger with the sender being the Button.
My question is: Why isn't the event being raised on the children as I've read that the Tunneling routing strategy is supposed to work and what can I do to fix it?
Also, the MouseEnter and MouseLeave events seem to follow the Bubbling routing strategy, but the behavior more closely resembles what I want to do. Can I force this to use a Tunneling routing strategy?
edit:
In order to further explain the goal of this project:
What I am intending to do, is to have a more complete understanding of Event Tunneling in WPF.
This morning, I examined the book more and found a way to make this work when the mouse enters the area of each specific child, which is an improvement.
Here is the new code within the C# Code-Behind File:
private void Two_OnMouseEnter(object sender, MouseEventArgs e)
{
Ellipse el = e.OriginalSource as Ellipse;
if (el != null)
{
el.Height = 60;
el.Width = 60;
el.Fill = Brushes.Orange;
}
TextBlock t = e.OriginalSource as TextBlock;
if (t != null)
{
t.Foreground = Brushes.Blue;
}
Grid g = e.OriginalSource as Grid;
if (g != null)
{
g.Height = 200;
g.Width = 200;
}
}
The difference with this code is that it uses the MouseEventArgs e object within the method signature, casting e.OriginalSource as the object type instead of casting the sender object.
In addition, this code is called using the PreviewMouseMove Event call in the XAML file:
<Button x:Name="Two" PreviewMouseMove="Two_OnMouseEnter">
which appears to only allow the contents to change when the mouse enters the area, but not when the mouse leaves the area. Which leads me back to part of my original question: can I force MouseEnter and MouseLeave to follow the Tunneling Routing Strategy?
It's not doing anything because the event is coming from the Button (which is therefore the sender).
Something like this would be what you are trying to do:
private void Two_OnMouseEnter(object sender, MouseEventArgs e)
{
Button b = sender as Button;
Grid g = b.Content as Grid;
Ellipse el = g.Children[0] as Ellipse;
Label l = g.Children[1] as Label;
g.Height = 200;
g.Width = 200;
el.Height = 60;
el.Width = 60;
el.Fill = Brushes.White;
l.Foreground = Brushes.Black;
}
UPDATE
To explain the tunneling strategy a bit more, consider the following example (I've removed the event handlers for "One" for convenience):
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Button x:Name="One" Content="ONE" Height="50" Width="Auto" />
<Button x:Name="Two">
<Grid PreviewMouseMove="Grid_PreviewMouseMove">
<Ellipse Fill="Black" Height="40" Width="40" PreviewMouseMove="Ellipse_PreviewMouseMove" />
<Label Content="TWO" Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center"
PreviewMouseMove="Label_PreviewMouseMove"/>
</Grid>
</Button>
</StackPanel>
</StackPanel>
And in the code-behind:
private void Grid_PreviewMouseMove(object sender, MouseEventArgs e)
{
Grid g = sender as Grid;
if (g != null)
{
g.Height = 200;
g.Width = 200;
}
System.Diagnostics.Debug.WriteLine("Sender: " + sender.GetType() + "; Source: " + e.Source.GetType());
}
private void Ellipse_PreviewMouseMove(object sender, MouseEventArgs e)
{
Ellipse el = sender as Ellipse;
if (el != null)
{
el.Height = 60;
el.Width = 60;
el.Fill = Brushes.White;
}
System.Diagnostics.Debug.WriteLine("Sender: " + sender.GetType() + "; Source: " + e.Source.GetType());
}
private void Label_PreviewMouseMove(object sender, MouseEventArgs e)
{
Label l = sender as Label;
if (l != null)
{
l.Foreground = Brushes.Black;
}
System.Diagnostics.Debug.WriteLine("Sender: " + sender.GetType() + "; Source: " + e.Source.GetType());
}
These methods could theoretically be refactored into one method, with some logic applied to detect the Type of the sender, but it would still be in the XAML three times.
One thing to notice is that the Grid won't fire its own events unless you set it's Background property (e.g., to Transparent), though it will still fire when the Ellipse and Label fire.
Another thing to notice is that when the Label fires the event, i.e. when you move the mouse over the Label, the event handler for the Ellipse is not called. This is because the tunneling strategy proceeds logically down the tree, rather than visually. If you look at the "Source: " part in the message in the Output window you'll see what I mean. The same would also be true for bubbling events.
As a general observation, the thing with this kind of strategy is that with events firing from different elements, your control isn't really functioning as one single Button, which looks to be the intention. If you move the mouse in slowly, you'll notice the Ellipse fires first, then the Label once you reach that.
There aren't any PreviewMouseEnter or PreviewMouseLeave events, so tunneling couldn't be used for that anyway.

get the source of image when user clicks it in c#

i have included four photos in xaml code as follows
<Image Grid.Column="0"
Source="Assets/1.png"
Name="m1"
MouseLeftButtonDown="selected"/>
<Image Grid.Column="1"
Source="Assets/2.png"
Name="m2"
MouseLeftButtonDown="selected"/>
<Image Grid.Column="2"
Source="Assets/3.png"
Name="m3"
MouseLeftButtonDown="selected"/>
<Image Grid.Column="3"
Source="Assets/4.png"
Name="m4"
MouseLeftButtonDown="selected"/>
i want to get the source of the image in "selected" function.
my selected function is as follows
private void selected(object sender, MouseButtonEventArgs e)
{
//do somethings....
}
How can i assign the source of the selected image(sender) to a new Image object?.
something similar to follows
Image newimage = new Image();
newimage.Source = //something..
Is there a way to dynamically get the source?
Cast your sender as an image and you will be able to use the Source property:
private void selected(object sender, MouseButtonEventArgs e)
{
Image newimage = new Image();
newimage.Source = ((Image)sender).Source;
}
Use OriginalSource property of event and cast it to Image:
var clickedImage = (Image)e.OriginalSource;
Image newimage = new Image();
newimage.Source = clickedImage.Source;

How to add grid background Image in c#

I am trying to change images source or image background in c# when a button is click with xaml design UI.
<Grid x:Name="gridimage">
<Image x:Name="Image" stretch="Fill"/>
</Grid>
private void Button1_click(object sender, RoutedEventArgs e)
{
// NOT WORKING FOR ME
gridimage.Source = new BitmapImage (new Uri("location"));
gridimage.Background = ?
}
Assumed that there is an Assets folder in your Visual Studio project that contains an image file 2.png, and that the image file's Build Action is set to Resource, you would create a BitmapImage in code behind from a Pack URI like this:
var uri = new Uri("pack://application:,,,/Assets/2.png");
var bitmap = new BitmapImage(uri);
Then you would use the BitmapImage as Source of your Image control:
Image.Source = bitmap;
Or use it with an ImageBrush as Background of your Grid:
gridimage.Background = new ImageBrush(bitmap);

WPF C# Media Element Show first image of video

i have a .mov file that i want to play using MediaElement of WPF , i can play and pause with no worries as i use MediaState.Manual , but i want to show the first image or frame of the video when i load it , the source is set in code behind , i tried MediaElement.ScrubbingEnabled = true both code behind and xaml but it still doesn't show.
Here is my code ( xaml side ) :
<DockPanel Height="386" HorizontalAlignment="Center" Name="dockPanel1" VerticalAlignment="Top" Width="731">
<MediaElement Name="McMediaElement" LoadedBehavior="Manual" UnloadedBehavior="Manual" Stretch="Fill" MediaOpened="Element_MediaOpened" MediaEnded="Element_MediaEnded" OpacityMask="#FF040410" Height="386" IsVisibleChanged="SingAlong_IsVisibleChanged" ScrubbingEnabled="True"></MediaElement>
</DockPanel>
Code behind ( xaml.cs) :
private void PlayAudio()
{
McMediaElement.LoadedBehavior = MediaState.Manual;
McMediaElement.Source = new Uri("../../SingAlong/GrassHopper and Ants/ants2.mov", UriKind.RelativeOrAbsolute);
McMediaElement.ScrubbingEnabled = true;
McMediaElement.Play();
}
private void button1_Click_1(object sender, RoutedEventArgs e) // Play button
{
if (McMediaElement.Source != null)
{
McMediaElement.Play();
}
else
PlayAudio();
}
private void button2_Click(object sender, RoutedEventArgs e) // Pause button
{
McMediaElement.Pause();
}
From what I can gather, you are loading your video (setting the Source) only when you click button1, yet you want the first frame to show before this happens. To accomplish this, you will have to load your video in another method, preferably when your Page or Window loads. Then you can do the following:
McMediaElement.ScrubbingEnabled = true;
McMediaElement.Play();
McMediaElement.Pause();

Categories

Resources