WPF - Trigger event if cursor is at certain position in image - wpf

I'm trying to trigger an event if the cursor is located in a certain part of an image in WPF. I thought I might try to "draw" an rectangle over the image and use a mousein/out event. I currently don't know how to accomplish this.
So for example I want to trigger a tooltip if the cursor enters the red rectangle of the image.
Currently I'm using the <Image></Image> Tag in WPF but that does not seem to work out....
Any help is appreciated!

I would probably utilize, that MouseEventArgs.GetPosition gives me the position relative to the object I hand over as an argument. Furthermore I would try to use the scaling of the image so that this approach is robust to later changing of the image size.
This approach would still give you a bunch of Magic Numbers in your code, but I don't think that can be avoided.
Here is a quick example showcasing what I mean.
Final result
Here is a gif showcasing the result of the approach, notice the
Yup this is an owl
appearing, when we are over a certain part of the image.
MainWindow.xaml
<Window x:Class="MousePosition.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:MousePosition"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid ShowGridLines="True" MouseMove="MousePosition">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="100"/>
<RowDefinition Height="*"/>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>
<TextBlock Grid.Column="1" x:Name="Feedback"/>
<Image x:Name="PrettyOwl" Grid.Column="1" Grid.Row="1" Source="Images/10-dithering-opt.jpg"/>
<TextBlock Grid.Column="1" Grid.Row="2" x:Name="WeAreThere"/>
</Grid>
MainWindow.xaml.cs
using System.Windows;
using System.Windows.Input;
namespace MousePosition
{
/// <summary>
/// Interaktionslogik für MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void MousePosition(object sender, MouseEventArgs e)
{
var scale = PrettyOwl.ActualWidth / PrettyOwl.Source.Width;
Point p = e.GetPosition(PrettyOwl);
Feedback.Text = string.Format("GetPosition(Mouse): X = {0}, Y = {1}", p.X, p.Y);
if (p.X > 100*scale && p.X < 200*scale && p.Y > 100*scale && p.Y < 200*scale)
{
WeAreThere.Text = "Yup, this is an owl";
}
e.Handled = true;
}
}
}
Hope that helps!

I thought I might try to "draw" an rectangle over the image and use a mousein/out event. I currently don't know how to accomplish this.
You could put the Image and an invisible element (with a background of Transparent) inside the same Grid:
<Grid>
<Image Source="screen.png" />
<Grid Background="Transparent" Width="10" Height="10" Margin="-40,-40,0,0"
MouseEnter="Grid_MouseEnter"
MouseLeave="Grid_MouseLeave"/>
</Grid>
The portion of the Image to be interactive is defined using the Margin property of invisible overlay element.

Related

how can i access the textbox inside the datagrid using the events of that datagrid?

I have a textbox in a datagrid that is designed using xaml. Can I access the textbox which has been designed in xaml previously in codefile using the events of the datagrid. Please help me.....................
<Window x:Class="GridTextBox.Test"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" WindowState="Maximized"
Title="Test" Height="300" Width="300" Loaded="Window_Loaded">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".25*"/>
<ColumnDefinition Width=".25*"/>
<ColumnDefinition Width=".25*"/>
<ColumnDefinition Width=".25*"/>
</Grid.ColumnDefinitions>
<DataGrid Grid.Row="1" Grid.Column="1" Name="datagrid1" SelectionChanged="datagrid1_SelectionChanged" LoadingRowDetails="DataGrid_LoadingRowDetails" Height="auto" Width="auto">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Name="txtEmpid" Text="hiiiiii"></TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
Firstly, you need to get use ItemContainerGenerator to get the correct row from data (in your datagrid1_SelectionChanged event).
var row = (DataGridRow)datagrid1.ItemContainerGenerator.
ContainerFromItem(datagrid1.SelectedItem);
then get the TextBlock like this:
var text = datagrid1.Columns[0].GetCellContent(row) as TextBlock;
var cellInfo=dataGrid1.SelectedCells[0];
var txt=cellInfo.Column.GetCellContent(cellInfo.Item);
or there is one more solution to get the text for all text box where there are dynamic rows are there.
DataRowView dataRow = (DataRowView)dataGrid1.SelectedItem;
int Columnindex = datagrid1.CurrentCell.Column.DisplayIndex;
int iGridRowsCount = ArgumentsDataGridforTestcasessTab.Items.Count;
for (int jRow = 0; jRow <= iGridRowsCount - 1; jRow++)
{
DataGridCell cell = GetCell(jRow, Columnindex);
ContentPresenter _contentPresenter = new ContentPresenter();
_contentPresenter = (ContentPresenter)cell.Content;
// get the attached control from the cell
TextBox myTextBox = GetVisualChild<TextBox>(_contentPresenter);
}
Pay attention whether the cell is in edit mode, or only in preview mode. If it is in edit mode (I mean you are writing into it, or you just wrote something and did not move to other cell) it won't cast to TextBlock, but to a Textbox.
When (only) it is in preview mode, it will cast to TextBlock, indeed.
I ran in the same issue and I resolved it with a try-catch sequence upon the casting the content to Textbox.
Did something like
try
{
var myTextboxVariable=(Textbox) datagridcell.Content
//extract the text out of myVariable.Text, do whatever you intend to the Textbox
}
catch(Exception ex)
{
var myTextBlockVariable=(TextBlock) datagridcell.Content
//extract the text out of myVariable.Text, do whatever you intend to the TextBlock
}
I know it looks like a brute solution, and there must be a more elegant way. But in the end it is the outcome that matters. Time is a coordinate in our dimension :)

how to put array of user control in the columns of Grid

Hi I have array of Employee data which includes photo,description, title name,salary. I have to show each employee data in separate panel control structure that means if I have 10 employees there will be 10 panels. and I have to show these panels in a grid which has two columns. Also width of the panel in each column will vary according to main page size.
Why not represent the employee data with a separate userControl then use a Wrap panel to display the employees. This approach would handle a variable number of employees.
To better illustrate my idea here's some code:
The MainPage has a grid with a WrapPanel that fills the available space.
MainPage.xaml
<UserControl
x:Class="SilverlightApplication2.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"
xmlns:ct="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"
mc:Ignorable="d">
<Grid x:Name="LayoutRoot" Background="WhiteSmoke">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="200"/>
<RowDefinition/>
<RowDefinition Height="200"/>
</Grid.RowDefinitions>
<ct:WrapPanel x:Name="panel1" Grid.Row="1" Grid.Column="1"
Background="Aqua" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"/>
</Grid>
</UserControl>
MainPage.xaml.cs
public partial class MainPage : UserControl
{
public MainPage ()
{
InitializeComponent();
this.DataContext = this;
this.Loaded += new RoutedEventHandler( MainPage_Loaded );
}
void MainPage_Loaded ( object sender, RoutedEventArgs e )
{
for ( int x = 0; x <= 10; x++ )
{
panel1.Children.Add( new ChildControl() );
}
}
}
I am adding ChildWindow controls as defined below to show how the WrapPanel adapts the presentation of its children to the space available.
ChildWindow.xaml
<UserControl
x:Class="SilverlightApplication2.ChildControl"
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"
Width="100"
Height="100">
<Grid x:Name="LayoutRoot" Background="PowderBlue">
<TextBlock Text="ChildControl"/>
</Grid>
</UserControl>
If I've missed your point please give some more clarification.

cloning the controls

I am designing a silverlight application in which i will have a rectangle control at the left side, when i click the rectangel and drag a copy of the rectangle control should be created and dragged and dropped in to the page.
Please can anyone help me with the code
For simplicity I'm going to leave out the Drag-Drop stuff since this question seems mainly about the cloning aspect.
The tool needed is the DataTemplate class. You place in a resource dictionary the set of items you want to clone each enclosed in a DataTemplate. You can use ContentPresenter to display instances of these items in say stack panel on the left. You can then use code to create instances of the template content and place them in say a Canvas on the right.
Example.
Xaml:-
<UserControl x:Class="SilverlightApplication1.CloningStuff"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<UserControl.Resources>
<DataTemplate x:Key="Rectangle">
<Rectangle Stroke="Blue" StrokeThickness="3" Fill="CornflowerBlue" Width="100" Height="75" />
</DataTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel>
<ContentPresenter x:Name="Rectangle" ContentTemplate="{StaticResource Rectangle}" />
</StackPanel>
<Canvas x:Name="Surface" MouseLeftButtonDown="Surface_MouseLeftButtonDown" Grid.Column="1" Background="Wheat">
</Canvas>
</Grid>
</UserControl>
Code:-
public partial class CloningStuff : UserControl
{
public CloningStuff()
{
InitializeComponent();
}
private void Surface_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Canvas target = (Canvas)sender;
Point p = e.GetPosition(target);
Rectangle r = (Rectangle)((DataTemplate)Resources["Rectangle"]).LoadContent();
Canvas.SetLeft(r, p.X);
Canvas.SetTop(r, p.Y);
target.Children.Add(r);
}
}
This shows using a ContentPresenter to display your rectangle. In place of drag-dropping (for which there are plenty of examples of elsewhere) this code just creates a Clone of the rectangle whereever the user clicks in the Canvas.

how can I enable scrollbars on the WPF Datagrid?

When I run the following Northwind WPF Toolkit Datagrid code from this article, I get a datagrid, but there are no scrollbars and hence the user can only see part of the datagrid. I am using the newest version March 2009.
What do I need to specify so that the WPF Datagrid has scrollbars?
I tried putting the datagrid in a ScrollViewer but that didn't help.
XAML:
<Window x:Class="TestDataGrid566.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:toolkit="http://schemas.microsoft.com/wpf/2008/toolkit"
Title="Window1" Height="600" Width="800">
<StackPanel>
<toolkit:DataGrid x:Name="TheDataGrid" AutoGenerateColumns="True"/>
</StackPanel>
</Window>
code-behind:
using System.Linq;
using System.Windows;
using TestDataGrid566.Model;
namespace TestDataGrid566
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
NorthwindDataContext db = new NorthwindDataContext();
var customers = from c in db.Customers
select c;
TheDataGrid.ItemsSource = customers;
}
}
}
Put the DataGrid in a Grid, DockPanel, ContentControl or directly in the Window. A vertically-oriented StackPanel will give its children whatever vertical space they ask for - even if that means it is rendered out of view.
WPF4
<DataGrid AutoGenerateColumns="True" Grid.Column="0" Grid.Row="0"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto">
</DataGrid>
with : <ColumnDefinition Width="350" /> & <RowDefinition Height="300" /> works fine.
Scrollbars don't show with <ColumnDefinition Width="Auto" /> & <RowDefinition Height="300" />.
Also works fine with: <ColumnDefinition Width="*" /> & <RowDefinition Height="300" />
in the case where this is nested within an outer <Grid>.
If any of the parent containers RowDefinition Height set to "Auto" also stoppers for scrollbars
Alternatively you may set Height "*"
Which happened in my case.
Adding MaxHeight and VerticalScrollBarVisibility="Auto" on the DataGrid solved my problem.
Add grid with defined height and width for columns and rows. Then add ScrollViewer and inside it add the dataGrid.
In my case I had to set MaxHeight and replace IsEnabled="False" by IsReadOnly="True"
This worked for me.
The key is to use * as Row height.
<Grid x:Name="grid">
<Grid.RowDefinitions>
<RowDefinition Height="60"/>
<RowDefinition Height="*"/>
<RowDefinition Height="10"/>
</Grid.RowDefinitions>
<TabControl Grid.Row="1" x:Name="tabItem">
<TabItem x:Name="ta"
Header="List of all Clients">
<DataGrid Name="clientsgrid" AutoGenerateColumns="True" Margin="2"
></DataGrid>
</TabItem>
</TabControl>
</Grid>

Allow TextBox to be resized but not to grow on user input

I have a TextBox defined inside a window like so:
<Window x:Class="NS.MainWindow"
...
SizeToContent="WidthAndHeight">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition MinWidth="200" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition MinHeight="50" />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0">Description:</TextBlock>
<TextBox Grid.Column="1" Grid.Row="0" TextWrapping="WrapWithOverflow" />
</Grid>
</Window>
The problem is that when the user types in the TextBox it expands to the right since only the MinWidth is set. What I really want is the text to wrap to the next line. I can get it to do this if I change the MinWidth on the column to be Width instead. However if I do this, then the TextBox no longer resizes when the Window is resized.
Is there a way I can have both? (i.e. resize only on Window resize, otherwise wrap)
The reason you're having this behavior is because you've set the Window's SizeToContent property - which basically authorizes the Window to resize itself based on the size requested by its content. So as you type in more stuff, the textbox says I need more space, the window obediently grows. Your textbox would not grow if you don't set the SizeToContent property.
So I'd say lose the SizeToContent property setter & Use proportional grid sizing. Here I say make Column#2 twice the width of Column#1. The default "Stretch" value of HorizontalAlignment and VerticalAlignment for the Grid should ensure that your controls resize correctly on a window resize.
<Window ...
Title="MyWindow" WindowStyle="ToolWindow" ResizeMode="CanResizeWithGrip"
MinWidth="300" Width="300" Height="80">
<Grid x:Name="myGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" MinWidth="100"/>
<ColumnDefinition Width="2*" MinWidth="200" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition MinHeight="50" />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0">Description:</TextBlock>
<TextBox Grid.Column="1" Grid.Row="0" TextWrapping="WrapWithOverflow"/>
</Grid>
If you just add the SizeToContent property setter back to above code snippet... you'd see some weird behavior where the textbox initially grows with text content.. however if you resize the window once.. the textbox would stop growing. Strange... can't explain that behavior.
HTH
WPF's TextBox doesn't seem to have that option built-in.
To solve this problem, you can use a custom TextBox that reports a desired (0, 0) size. It's an ugly hack, but it works.
In your MainWindow.xaml.cs file:
namespace NS
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
...
}
// Ugly HACK because the regular TextBox doesn't allow autoresize to fit the parent but NOT autoresize when the text doesn't fit.
public class TextBoxThatDoesntResizeWithText : TextBox
{
protected override Size MeasureOverride(Size constraint)
{
return new Size(0, 0);
}
}
}
Then, in your MainWindow.xaml file:
<Window x:Class="NS.MainWindow"
...
xmlns:local="clr-namespace:NS"
SizeToContent="WidthAndHeight">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition MinWidth="200" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition MinHeight="50" />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0">Description:</TextBlock>
<local:TextBoxThatDoesntResizeWithText Grid.Column="1" Grid.Row="0" TextWrapping="WrapWithOverflow" />
</Grid>
</Window>
Change the second ColumnDefinition to be Width="*".

Resources