PreviewMouseDown Routing to Border/Textblock Instead of Button - c#

I'm trying to access the text of a button when I click on it. All the buttons are created by a Data Template. The buttons are inside a UniformGrid which is inside an ItemsControl:
<ItemsControl x:Name="uniformControl">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid MouseDown="Button_PreviewMouseDown">
</UniformGrid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Then in my code behind (my Operations class that has the binding content isn't included):
public MainWindow()
{
InitializeComponent();
uniformControl.PreviewMouseDown += new MouseButtonEventHandler(Button_PreviewMouseDown);
uniformControl.ItemsSource = Operations.operations;
}
private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var clickedElement = e.OriginalSource;
Debug.WriteLine(clickedElement);
string keyword = (e.Source as Button).Content.ToString();
Debug.WriteLine(keyword);
}
Although, instead of the OriginalSource being a button, it is either a textblock or a border. I'm assuming this is due to textblock and border being the specific part of the button being clicked, since PreviewMouseDown traces the path to the smallest child element from what I understand.
The line where I assign a value to keyword throws a NullRefernceExcpetion as well.
I've tried using MouseDown instead, but the method didn't even run when I tried that.
Any ideas? Thanks for your time.

You could just use the Button.Click event as an attached event on your UniformGrid, e.g. like this:
<UniformGrid Button.Click="Button_Click">
</UniformGrid>
And then access the button using OriginalSource:
private void Button_Click(object sender, RoutedEventArgs e)
{
var clickedElement = e.OriginalSource;
Debug.WriteLine(clickedElement);
}

Related

Horizontal ListBox multiple lines (WPF)

I want to implement a horizontal ListBox, with multiple lines.
Using WrapPanel:
<ListBox>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
I get the following result:
But I am looking for this result:
How can I implement this?
Based on your comments, I made a UniformGrid which adjusts the number of rows/columns, whenever the window is resized.
MainWindow.xaml:
<Grid x:Name="Container">
<ListBox HorizontalContentAlignment="Stretch">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid x:Name="ItemsGrid" Loaded="ItemsGrid_Loaded" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
The names and events are important. They are used for adjusting the columns/rows in the code file.
MainWindow.xaml.cs
public partial class MainWindow : Window
{
private UniformGrid itemsGrid;
public MainWindow()
{
InitializeComponent();
}
private void ItemsGrid_Loaded(object sender, RoutedEventArgs e)
{
// Set reference and adjust columns/rows once the UniformGrid is loaded.
itemsGrid = sender as UniformGrid;
((UniformGrid)sender).Columns = (int)(Container.ActualWidth / 250);
((UniformGrid)sender).Rows = (int)(Container.ActualHeight / 75);
}
private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
// Adjust number of columns/rows whenevner the window is resized.
if (itemsGrid != null)
{
itemsGrid.Columns = (int)(Container.ActualWidth / 250);
itemsGrid.Rows = (int)(Container.ActualHeight / 75);
}
}
}
This could definitely be cleaned up a bit, but it seems to do the job. There might be more elegant solutions, but I couldn't find any...

WPF: Moving from one cell to the next when pressing Enter in an ItemsControl

I have a WPF control defined as follows:
<ItemsControl ItemsSource="{Binding DutyValueBinders}" IsEnabled="{Binding Enabled}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Value, TargetNullValue=''}" Width="50"></TextBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
This produces a grid, looking like this:
So although there's only one text box defined in the XAML, the user sees a grid.
What I want to do is, after the user has typed a value and pressed Enter or Return, focus should pass to the next cell and so on until the end of the grid is reached. I tried putting this routine into the code behind (copied from another thread on Stackoverflow):
private void UserControl_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key != Key.Enter || (!e.IsToggled && sender is Button)) return;
var request = new TraversalRequest(FocusNavigationDirection.Next);
var keyboardFocus = Keyboard.FocusedElement as UIElement;
if (keyboardFocus == null) return;
keyboardFocus.MoveFocus(request);
e.Handled = true;
}
and modified the header of the XAML as follows:
<UserControl ...
(Other references)
...
d:DesignHeight="300" d:DesignWidth="300" PreviewKeyDown="UserControl_PreviewKeyDown">
The trouble is, as there's only one text box in the XAML definition, there's no tabindex order for it to follow, and so when you press Enter the cursor just vanishes.
Is there any way to do this, short of scrapping the XAML entirely and starting again with a different type of control?
How about handling the PreviewKeyDown event for the ItemsControl like this?:
<ItemsControl ItemsSource="{Binding DutyValueBinders}" IsEnabled="{Binding Enabled}"
PreviewKeyDown="ItemsControl_PreviewKeyDown">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Value, TargetNullValue=''}" Width="50"></TextBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
private void ItemsControl_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
TextBox textBox = Keyboard.FocusedElement as TextBox;
if (textBox != null)
{
textBox.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
}
}

c# - Find and modified image control by gridview selecteditem

I need to change the source of an Image control inside a gridview datatemplate when a click event is raised in uwp. When i click on a car image, this Image needs to be modified and displayed the brand logo. I succeed with that code:
<controls:AdaptiveGridView x:Name="AdaptiveGridViewControl"
animations:ReorderGridAnimation.Duration="350"
Margin="0,12,0,0"
ItemHeight="200"
DesiredWidth="200"
SelectionMode="Single"
IsItemClickEnabled="True"
ItemClick="GridView_ItemClick"
>
<controls:AdaptiveGridView.ItemTemplate>
<DataTemplate x:DataType="data:MyData">
<StackPanel Orientation="Horizontal">
<controls:ImageEx x:Name="ImageExControl"
MaxHeight="200"
IsCacheEnabled="True"
Source="{x:Bind carsPictures}"
Stretch="Fill"
PlaceholderSource="/Assets/placeholder_cover.jpg"
PlaceholderStretch="Uniform"/>
</StackPanel>
</DataTemplate>
</controls:AdaptiveGridView.ItemTemplate>
</controls:AdaptiveGridView>
Thanks to that post : How to access a Control inside the data template in C# Metro UI in the code behind,
i can use FindChildControl(DependencyObject control, string ctrlName) method.
In code behind:
private void GridView_ItemClick(object sender, ItemClickEventArgs e)
{
var newData = (MyData)e.ClickedItem;
ImageEx ex = FindChildControl<ImageEx>(this, "ImageExControl") as ImageEx;
ex.Source = newData.brandLogo;
}
The problem is this gridview contains 30 cars picture and only the first Image control is modified when a click event is raised. I don't know how to use the AdaptiveGridView.SelectedItem to change the clicked Image control.
You need to Get GridViewItem that has been Clicked so that you can change the image of the Clicked Item. Change your ItemClick Event to something like below.
private void GridView_ItemClick(object sender, ItemClickEventArgs e)
{
GridViewItem item = myGridView.ContainerFromItem(e.ClickedItem) as GridViewItem;
MyData newData = (MyData)e.ClickedItem;
ImageEx ex = FindChildControl<ImageEx>(item, "ImageExControl") as ImageEx;
ex.Source = newData.brandLogo;
}
Hope This Helps.

Get textBlock text that is inside a datatemplate from C# (XAML)

i want to do some coding on textblock tap event for which, i required its text value
my xaml is like below
<ListBox x:Name="listBox1">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Height="auto" >
<TextBlock Name="myTextBlock" Text="{Binding Busnumber}" Tap="buss_Tap" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
and then on textblock tap event i want to get tapped clicked text value
private void buss_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
//i want to achieve this
// string aa= myTextBlock.text;
//but this is not working so what to do here to achieve the same?
}
Try this:
private void buss_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
TextBlock txt = (TextBlock)sender;
MessageBox.Show(txt.Text);
}
You will get TextBlock in sender parameter. Typecast it to Textblock and can get text from there:
string text = ((TextBlock)sender).Text;

How to get the absolute screen coordinates of an element placed inside a DataTemplate

I have a ListView that uses a UserControl as DataTemplate:
<ListView>
<ListView.ItemTemplate>
<DataTemplate>
<views:TaskerInfoView />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I need the absolute screen coordinates of a Button placed inside the TaskerInfoView usercontrol.
I tried with this code:
var newButtonPoint = button.PointToScreen(new Point(0, 0));
but I received the exception "This Visual is not connected to a PresentationSource" I suppose because the button is inside a DataTemplate so it's not connected to a visual element.
UPDATE
I've found why I had received that exception: before the PointToScreen call I move the item inside the ObersvableCollection, that is the ItemsSource of the ListView:
this._taskList.Move(currentIndex, currentIndex - 1); // _taskList is the ObservableCollection<>
var newButtonPoint = button.PointToScreen(new Point(0, 0));
If I remove the ObservableCollection<>.Move, the PointToScreen works correctly.
I think that internally the ObservableCollection<>.Move removes the item and inserts another one in the new position. The exception is raised because button contains a reference to the removed element that is actually disconnected from the TreeVisual
For example :
xaml :
<ListBox x:Name="myListBox" SelectedIndex="2">
<ListBoxItem>1</ListBoxItem>
<ListBoxItem>2</ListBoxItem>
<ListBoxItem>3</ListBoxItem>
<ListBoxItem>4</ListBoxItem>
<ListBoxItem>5</ListBoxItem>
</ListBox>
<Button Content="GetIndexFromContainer" Click="Button_Click" />
cs :
private void Button_Click(object sender, RoutedEventArgs e)
{
var index = GetSelectedIndexFromList();
}
private int GetSelectedIndexFromList()
{
var container = myListBox.ItemContainerGenerator.ContainerFromItem(MyListBox.SelectedItem);
var index = myListBox.ItemContainerGenerator.IndexFromContainer(container);
return index;
}

Categories

Resources