</ItemsControl.ItemTemplate>
<ItemsControl.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="GroupItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupItem">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" HorizontalAlignment="Center" FontWeight="Bold" Foreground="{StaticResource Accent}" Text="{Binding Path=Name , StringFormat={}{0:D}}" />
<ItemsPresenter Grid.Row="1"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ItemsControl.GroupStyle>
</ItemsControl>
<Interactivity:Interaction.Triggers>
<Interactivity:EventTrigger EventName="ScrollChanged" >
<Presentation:InvokeDelegateCommandAction Command="{Binding ChatScrollViewer_OnViewChange}" CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=InvokeParameter}" />
</Interactivity:EventTrigger>
</Interactivity:Interaction.Triggers>
</ScrollViewer>
private void ChatScrollViewer_OnViewChangeEvent(ScrollChangedEventArgs Event)
{
ScrollViewer scrollViewer = Event.OriginalSource as ScrollViewer;
if (scrollViewer.VerticalOffset == 0)
{
}
}
When data binding take place scroll changed event is triggered . How check that this is a trigger from data binding and do nothing and when triggered manually do something
When the binding is updated, is it the ItemsSource of the scroll viewer?
Chances are the binding is adding or removing something, resulting in the scroll viewer changing the size of it's content. Take a look at the scroll changed event args, there's lot of conditions that trigger this event.
You probably only want to check the event properties like:
VerticalChange - Gets a value that indicates the change in vertical offset of a ScrollViewer.
So try only performing your action when VerticalChange != 0. Or drop a debugger in there and see if there's any specific event types you want to ignore that only happen when binding changes, for example:
ExtentHeightChange - Gets a value that indicates the change in height of the ScrollViewerextent.
Might only be none-zero when the binding has updated, making the scroll bar bigger (but not changing the scroll position).
So something like this:
private void ChatScrollViewer_OnViewChangeEvent(ScrollChangedEventArgs Event)
{
if (Event.VerticalChange != 0)
{
ScrollViewer scrollViewer = Event.OriginalSource as ScrollViewer;
if (scrollViewer.VerticalOffset == 0)
{
}
}
}
Hope that helps.
Related
I keep my theme file (ResourceDictionary) in a separate project. I'm using the following ControlTemplate structure for the DataGrid in my ResourceDictionary:
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" Margin="10 5" />
<Button x:Name="btnFilter" Content="" FontFamily="{StaticResource FontAwesome}" FontSize="16" />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Where I use DataGrid in the related project, I need to assign a click event to the button named btnFilter above, how can I do this?
Methods like VisualTree do not work very well. Since I have nearly 20 columns in Datagrid, I use horizontal scroll and VisualTree does not see the columns hidden by scroll.
What are the best practices I should follow here?
As Button.Click is a bubbling routed event, you can listen it at the control nearer to the root.
For example, assuming you name the Style as "HeaderStyle", it would be like the following.
<DataGrid ColumnHeaderStyle="{StaticResource HeaderStyle}"
Button.Click="Button_Click">
<DataGrid.Columns>
<DataGridTextColumn Header="HeaderText"/>
</DataGrid.Columns>
</DataGrid>
In case there are multiple Buttons in that control, you can filter by checking RoutedEventArgs.OriginalSource.
private void Button_Click(object sender, RoutedEventArgs e)
{
if (e.OriginalSource is Button { Name: "btnFilter" })
{
// Handle event here.
}
}
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 am working on a project in C# WPF. I have a tab container and I want to dynamically load different types of tabs into the tab container as the user requires. As an example I am doing something like the following:
tabContainer.Items.Add(new MyUserControl());
I want each tab to have a close button so the tab can be removed the container when the user no longer requires it.
I found this code project example but from what I can see you are a loading a user control which contains the xaml for the tab itself, not the tab content or am I missing something.
How can I load in my User Control into the tab container, but also have the tab closable.
Currently the tab that I am loading in uses some static binding to set the tab title using the following:
<TabControl x:Name="tabContainer" Grid.Column="2" Margin="10,45,0,0" RenderTransformOrigin="0.5,0.55" Grid.ColumnSpan="3">
<TabControl.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Header" Value="{Binding TabHeader}" />
</Style>
</TabControl.Resources>
</TabControl>
My user control then has a `public string TabHeader{get;set;} which gets set in the constructor depending on what constructor of my user control is used.
You will have to define the close Button yourself. You could for example do this in the HeaderTemplate of the TabItem:
<TabControl x:Name="tabContainer">
<TabControl.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Header" Value="{Binding TabHeader}" />
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" />
<Button Content="x" Click="Button_Click_2"
Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType=TabItem}}"/>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Resources>
</TabControl>
The Tag property is bound to the UserControl in the Items collection which you can remove in the click event handler of the Button, like this:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
tabContainer.Items.Add(new MyUserControl());
}
private void Button_Click_2(object sender, RoutedEventArgs e)
{
Button button = sender as Button;
tabContainer.Items.Remove(button.Tag);
}
}
If you want to add a close button to each tab, that would be in the TabItem style ControlTemplate. Normally you'd specify the data context (i.e. the data only that's driving the content) in Content and then specify the look in ContentTemplate. If your Content is a UserControl then you don't specify the ContentTemplate since a UserControl knows how to draw itself.
For my sins, I've added close-tab buttons to the WPF TabControl. I ended up putting the close button in the ItemTemplate. Here's a minimal version that works with the way you're populating the TabControl and the header content:
<TabControl
>
<TabControl.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Header" Value="{Binding TabHeader}" />
</Style>
</TabControl.Resources>
<TabControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label
Content="{Binding}"
Grid.Column="0"
/>
<Button
VerticalAlignment="Center"
Grid.Column="1">
<Path
Data="M 0, 0 L 12, 12 M 12,0 L 0,12"
Stroke="Red"
StrokeThickness="2"
Width="12"
Height="12"
/>
</Button>
</Grid>
</DataTemplate>
</TabControl.ItemTemplate>
<local:UserControl1 TabHeader="First Item" />
<local:UserControl1 TabHeader="Second Item" />
</TabControl>
I have simple listbox here: link to image where I have binded data. Now when I tap on one item(one row) listbox item/row is selected/violet color but in fact this item is not real selected only row change color but when I click on image or text then row where I click in not selected/violin but item in code is selected. I'm not sure if You understand what I'm saying. In short if I click on blank space row then row is visual selected but sender like listbox item not get data, if I click on text or image then sender get data but its not visual selected. How I can do it when I click anywhere row is selected/violet and item-sender get data?
And my second question is why my rectangle used like line has max width like text I want strech this rectangle at full width of listbox is this possible?
XAML:
<ListBox x:Name="lbMessagesUsersList" Foreground="Black" ItemsSource="{Binding MyDatasMessagesUserList }">
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem Tapped="userTapped" Tag="{Binding}" >
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="{Binding MessengeHisPhoto}" HorizontalAlignment="Left" Width="40" Height="40" Margin="5,-18,0,-18" Stretch="Fill" ></Image>
<TextBlock x:Name="tbMessengerName" Text="{Binding MessengeFullName}" HorizontalAlignment="Left"
Grid.Column="1" Margin="25,0,0,0"/>
</Grid>
<Rectangle Height="1" Margin="0,0,0,-38" HorizontalAlignment="Stretch">
<Rectangle.Fill>
<SolidColorBrush Color="Black"/>
</Rectangle.Fill>
</Rectangle>
</StackPanel>
</ListBoxItem>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
CS:
private void userTapped(object sender, TappedRoutedEventArgs e)
{
var button = sender as ListBoxItem;
if (button != null)
{
var subject = MyDatasMessagesUserList.FirstOrDefault(sub => sub == button.Tag);
if (subject != null)
{
IdOfChoosenUser = subject.MessengeIdUser;
}
}...
I also try remove ListbOxItem and set binding tag to stackPanel but this dont work too.{
Note that, when you use an ItemTemplate, each item gets wrapped in a ListBoxItem -- so, you shouldn't use a ListBoxItem in the template, as that will produce two nested ones. Try removing it, like so:
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Tapped="userTapped" Tag="{Binding}">
<!-- content here -->
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
For the handler, it might be easier to simply reference the ListBox by name instead of trying to use Tag (thanks #MetroSmurf):
private void userTapped(object sender, TappedRoutedEventArgs e)
{
var selectedItem = lbMessagesUsersList.SelectedItem;
var subject = MyDatasMessagesUserList.FirstOrDefault(sub => sub == selectedItem);
}
For the second part of your question: to stretch the items, you need to stretch the ListBoxItem wrapper. Do this by using ItemContainerStyle:
<ListBox>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>