I have this ItemsControl:
<ItemsControl Grid.Row="1" ItemsSource="{Binding Board}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="local:Square">
<Rectangle Stroke="Blue" StrokeThickness="0.5" Width="{Binding Width}" Height="{Binding Height}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
and it simply draws squares onto the screen. I want an event so that when I click on one of the squares it calls that event, I need to also get the object I clicked on (the data type of the template is a Square class and the entire grid is bound to an observable collection called Board), how could I do that?
Put the rectangle in the template of a Button and handle the button's click event. Remember to set the rectangle's Fill to Transparent, or else mouse clicks in the fill area of the button won't be detected.
<Button Click="Rectangle_Click">
<Button.Template>
<ControlTemplate TargetType="Button">
<Rectangle
Fill="Transparent"
Stroke="Blue"
StrokeThickness="0.5"
Width="{Binding Width}"
Height="{Binding Height}"
/>
</ControlTemplate>
</Button.Template>
</Button>
private void Rectangle_Click(object sender, RoutedEventArgs e)
{
var button = sender as Button;
var square = button.DataContext as Square;
// Do whatever
}
It would be preferable to give Square a command property, and bind Button.Command to that:
public class Square
{
// stuff
public ICommand SelectCommand { get; } // Initialize in constructor
// stuff
}
<Button Command="{Binding SelectCommand}">
<!-- ...as before... -->
But then you need to implement ICommand, etc. The Click event works well enough.
You could also handle MouseLeftButtonDown on the Rectangle itself. You'd still have to set its Fill to Transparent. I prefer this solution because Click behavior is more complicated than MouseLeftButtonDown: For example, when you mouse down on
a button and drag out of the button before releasing the mouse button, Click isn't raised. Users are accustomed to that behavior.
Related
I need to lock the Z-order of a canvas/content control after it is dragged by a Thumb.
In the below image, the "Joe Smith" pops above the others the other two ellipses while the the mouse over is active. Once the drag stops and the mouse moves out, it drops back to its value.
I am unable to find a solution within the design I have shown below to keep it locked above the others.
Minimal Reproducible Example
I have created a code gist of all code that contains the xaml, the thumb class and the people class. All one has to do is create a .Net Core WPF app and drop in the snippets of code and set name spacing in Xaml as needed.
Design
There is an ItemsControl which has DataTemplate defined with a Canvas. Each content in the ItemsControl has ContentControl which has a Thumb as applied by a style.
Another style trigger of the mouse entering the ContentPresenter has the content temporarily pushed in zIndex above the others by setting the content's internal Grid's zIndex to 1.
How to stick that zIndex?
Xaml
<ItemsControl Margin="10" ItemsSource="{Binding Source={StaticResource People}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="1" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Canvas>
<ContentControl Width="100" Height="100" >
<Grid>
<Ellipse Fill="Silver">
<Ellipse.Effect>
<DropShadowEffect Color="Black" Direction="320" ShadowDepth="6" Opacity="0.5"/>
</Ellipse.Effect>
</Ellipse>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Margin="3,3,3,0" Text="{Binding Path=First}"/>
<TextBlock Margin="3,0,3,7" Text="{Binding Path=Last}"/>
</StackPanel>
</Grid>
</ContentControl>
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style TargetType="{x:Type ContentPresenter}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Grid.ZIndex" Value="1"/>
</Trigger>
</Style.Triggers>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
See the Gist for all supporting classes and styles to reproduce
Actual Design
The ultimate goal is to have a panel of images of a set size, when the user grabs the thumb the image it will move forward and lock above the others. I say this in-case there is another way to do that which could provide an answer above the minimal example design.
In my ItemsControl I changed the ItemsPanelTemplate to be a Canvas such as
<ItemsPanelTemplate>
<Canvas x:Name="MainCanvas" />
</ItemsPanelTemplate>
Which when looking at the Visual Tree where the user was clicking the ContentControl, it had a parent of a Canvas that had a ContentPresenter with that top level Canvas such as (see named MainCanvas):
I changed the ContentControl to have a MouseEnter event :
<ContentControl Width="100" Height="100" MouseEnter="EnterMouse">
<Grid>
<Ellipse Fill="Silver">
In that method I needed to find the named "MainCanvas" Canvas and enumerate all of its children the ContentPresenters and extract the max ZIndex and then set my ContentControl (shown in blue above) to that ZIndex value plus 1.
Here is the code behind where extract the necessary the parents:
private void EnterMouse(object sender, MouseEventArgs e)
{
if (sender is ContentControl cc)
{
var cpParent = FindParent<ContentPresenter>(cc);
var p2 = FindParent<Canvas>(cpParent);
var max = p2.Children.Cast<UIElement>().Max(control => Panel.GetZIndex(control));
Panel.SetZIndex(cpParent, max + 1);
}
}
private T FindParent<T>(DependencyObject child) where T : DependencyObject
{
DependencyObject immediateParent = VisualTreeHelper.GetParent(child);
T parent = immediateParent as T;
return parent ?? FindParent<T>(immediateParent);
}
I have a ListBox bound to a collection of items of type Definition. My requirement is that every time the mouse is hovered over the area of a templated ListBoxItem, a second ListBox open next to the ListBoxItem, revealing sub-items which are of type Word.
(I am basically implementing something similar to a TreeView using two ListBoxes. This is for earlier versions so using a TreeView control is not an option.)
This is the data structure...
public class Word
{
public string Name { get; set; }
}
public class Definition
{
public string Name { get; set }
public ObservableCollection<Word> Words;
}
public class Dictionary
{
public string Name { get; set }
public ObservableCollection<Definition> Definitions;
}
And here is the XAML view...
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Button Grid.Row="0"
Height="0">
<Button.Flyout>
<Flyout x:Name="DefinitionFlyout"/>
<ListBox x:Name="WordsListBox" HorizontalAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemp late x:DataType="local:Word">
<TextBox TextWrapping="NoWrap"
Height="Auto"
BorderThickness="0"
HorizontalAlignment="Stretch"
Text="{x:Bind Name}"
TextAlignment="Left"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Flyout>
</Button.Flyout>
</Button>
<ListBox x:Name="DefinitionsListBox"
Grid.Row="1"
SelectionMode="Single"
HorizontalAlignment="Stretch">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate x:DataType="local:Definition">
<TextBox TextWrapping="NoWrap"
Height="Auto"
BorderThickness="0"
HorizontalAlignment="Stretch"
Text="{x:Bind Name, Mode=TwoWay}">
</TextBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
When the mouse pointer hovers over a Definition item in DefinitionsListBox, WordsListBox should fly out and display the Words of that Definition. And when the pointer exits that Definition and hovers over a new one, I want WordsListBox to reflect that change.
Unfortunately, I can't find the events that will help me accomplish this.
I thought defining PointerEntered and PointerExited in the TextBox of Definition would do the trick but they don't because PointerExited fires IMMEDIATELY after PointerEntered, as in almost simultaneously, and not when the mouse exits the TextBox area. And SelectionChanged of ListBox doesn't fire.
The first event should fire when ListBoxItem highlighting begins, and the second one, when the highlighting ends.
What do you recommend for this, please?
I thought defining PointerEntered and PointerExited in the TextBox of Definition would do the trick but they don't because PointerExited fires IMMEDIATELY after PointerEntered, as in almost simultaneously.
The problem is the when Flyout show at the button there is a mask layer cover on the window. This will prevent basic input event of TextBox defined. It looks PointerExited fires immediately after PointerEntered as in almost simultaneously.
For solving this , you could set OverlayInputPassThroughElement property for Flyout that make the area of ListBox could response PointerEntered PointerExited event when Flyout is opened. For more please refer the following code.
<Flyout x:Name="DefinitionFlyout" OverlayInputPassThroughElement="{x:Bind DefinitionsListBox}">
I have XAML:
<Grid MouseMove="onMouseMove" >
<ItemsControl Name="btnTableImageList">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Content}"
Height="{Binding Height}"
Width="{Binding Width}"
Tag="{Binding Tag}"
Margin="{Binding Margin}"
Background="{Binding Background}"
HorizontalAlignment="Center"
MouseDown="tblButton_MouseDown"
MouseUp="tblButton_MouseUp"
Click="ClickHandlerTableBtn"
TextBlock.TextAlignment="Center" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
And code behind:
private void onMouseMove(object sender, MouseEventArgs e)
{
lblCoord.Content = Mouse.GetPosition(Application.Current.MainWindow);
}
On the form there is Label named lblCoord, and there are two buttons that are created after form is loaded.
I want to display mouse coordinate in lblCoord in relation to the Grid, but coords are displayed only when i move mouse cursor over any of the buttons that are placed inside that grid.
My guess is that I am placing MouseMove="onMouseMove" in wrong place.
Thanks for your help.
It will work when you set Background of Grid to anything but Transparent
As default, Grid's background is transparent. When it is transparent, mouse events work when you set Background="Transparent" too.
Mouse events handled nearest parent element with background IMHO
I have an UWP app in Microsoft/Windows Store, and I want to add a popup. This popup appears when I click a button. Is it possible for the popup to have the Fluent Design System (transparent background)?
Yes, it is. The following example, is taken from their Microsoft's Popup class documentation, here:
Code-Behind:
// Handles the Click event on the Button inside the Popup control and
// closes the Popup.
private void ClosePopupClicked(object sender, RoutedEventArgs e)
{
// if the Popup is open, then close it
if (StandardPopup.IsOpen) { StandardPopup.IsOpen = false; }
}
// Handles the Click event on the Button on the page and opens the Popup.
private void ShowPopupOffsetClicked(object sender, RoutedEventArgs e)
{
// open the Popup if it isn't open already
if (!StandardPopup.IsOpen) { StandardPopup.IsOpen = true; }
}
XAML:
<Grid x:Name="Output" HorizontalAlignment="Left" VerticalAlignment="Top" Grid.Row="1">
<StackPanel>
<Button Content="Show Popup (using Offset)" Click="ShowPopupOffsetClicked"/>
</StackPanel>
<Popup VerticalOffset="10" HorizontalOffset="200" x:Name="StandardPopup">
<Border BorderBrush="{StaticResource ApplicationForegroundThemeBrush}"
Background="{StaticResource ApplicationPageBackgroundThemeBrush}"
BorderThickness="2" Width="200" Height="200">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="Simple Popup" FontSize="24.667" HorizontalAlignment="Center"/>
<Button Content="Close" Click="ClosePopupClicked" HorizontalAlignment="Center"/>
</StackPanel>
</Border>
</Popup>
</Grid>
By simply changing the Background dependency property of the Border object from:
Background="{StaticResource ApplicationPageBackgroundThemeBrush}"
to
Background="{StaticResource NavigationViewExpandedPaneBackground}"
your Popup will be set with an Acrylic Background (this one is an Acrylic Brush resource in the template of the NavigationView defined for one of its visual state).
Or you can always create your own Acrylic Brush resource:
<Grid.Resources>
<AcrylicBrush x:Name="myacrylicbrush" BackgroundSource="HostBackdrop"
TintColor="#FF0000FF"
TintOpacity="0.4"
FallbackColor="#FF0000FF"/>
</Grid.Resources>
And with this you can set the Border Background property to your custom resource, like this:
Background="{StaticResource myacrylicbrush}"
And adjust the settings to get the look your are striving for.
BackgroundSource is set to HostBackDrop, to use your Background Acrylic, the Tint overlay layer is set to a value which will be rather transparent, while the TintColor is set to full opaque blue.
Result:
If you want to define the Background property as an Acrylic brush of any control, if you are targeting an app with Falls Creator update, I think the question should probably be the other way around: Where am I not able to utilize the new released features?
<ItemsControl Name="CanvasTableMap" ItemsSource="{Binding}" ItemsPanel="{DynamicResource ItemsPanelTemplate1}" ItemTemplate="{DynamicResource DataTemplate1}">
<ItemsControl.Resources>
<ItemsPanelTemplate x:Key="ItemsPanelTemplate1">
<WrapPanel Background="{DynamicResource ContentBackground}" />
</ItemsPanelTemplate>
<DataTemplate x:Key="DataTemplate1">
<Button Canvas.Left="100" Content="{Binding Name}" Template="{DynamicResource ButtonTableTemplate}"></Button>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
Here is my code.No problem with that. I have created an adorner and i would like to add an adorner for each button when i want. It is a little difficult as i dont know how to get the Buttons. CanvasTableMap.Items returns the Model so i dont know how to get access to the controls efficiently.
An easy way to do that is to define a handler for the Loaded event of the button, and add the adorner in that handler:
XAML
<Button Canvas.Left="100" Content="{Binding Name}" ... Loaded="Button_Loaded" />
Code-behind
private void Button_Loaded(object sender, RoutedEventArgs e)
{
var button = (Button)sender;
var layer = AdornerLayer.GetAdornerLayer(button);
// Add the adorner
...
}
If you don't want to use code-behind, you can create an attached behavior (either with System.Windows.Interactivity or by creating an attached property)
You can use the ItemContainerGenerator to get the control created from the data (ContainerFromItem). Usually doing things that way is not such a good idea though.