I'm trying to display shapes in two separate Listboxes occupying the same space. I've set the Background to both Transparent and {x:Null} but the mouseclick is still getting captured by the topmost Listbox so I can't select any shapes from the underlying ListBox.
Here is some sample code reproducing the problem.
<Grid>
<!-- ListBox 1 -->
<ListBox Background="{x:Null}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="{x:Null}"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Transparent">
<Ellipse Width="100" Height="100" Stroke="Blue" StrokeThickness="10"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
1
</ListBox>
<!-- ListBox 2 -->
<ListBox Background="{x:Null}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="{x:Null}"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Canvas.Left" Value="100"/>
<Setter Property="Canvas.Top" Value="100"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Ellipse Width="100" Height="100" Stroke="Blue" StrokeThickness="10"/>
</DataTemplate>
</ListBox.ItemTemplate>
1
</ListBox>
</Grid>
This is how I solved this problem for now but I'm more than open for other suggestions :) I enabled hittesting on the Grid and disabled it for both ListBoxes. Then I did the hittesting in the event handler instead
<Grid MouseDown="Grid_MouseDown"
IsHitTestVisible="True"
Background="Transparent">
<!-- ListBox 1 -->
<ListBox Background="Transparent"
IsHitTestVisible="True"
..>
</ListBox>
<!-- ListBox 2 -->
<ListBox Background="Transparent"
IsHitTestVisible="True"
..>
</ListBox>
</Grid>
Event handler and hittesting
private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
{
Grid grid = sender as Grid;
Point ptCurrent = e.GetPosition(grid);
VisualTreeHelper.HitTest(grid, null, new HitTestResultCallback(HitTestCallback), new PointHitTestParameters(ptCurrent));
}
public HitTestResultBehavior HitTestCallback(HitTestResult htrResult)
{
ListBoxItem listBoxItem = GetVisualParent<ListBoxItem>(htrResult.VisualHit);
if (listBoxItem != null)
{
listBoxItem.IsSelected = true;
return HitTestResultBehavior.Stop;
}
return HitTestResultBehavior.Continue;
}
public T GetVisualParent<T>(object child) where T : Visual
{
DependencyObject c = child as DependencyObject;
while ((c != null) && !(c is T))
{
c = VisualTreeHelper.GetParent(c);
}
return c as T;
}
You can bind multiple sets of data to a single ListBox by using a CompositeCollection as the items source:
<ListBox ...>
<ListBox.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection={Binding ...}" />
<CollectionContainer Collection={Binding ...}" />
</CompositeCollection>
</ListBox.ItemsSource>
</ListBox>
You can then use data templates to control the appearance of different data types. You can either use implicit data templates or you can use an ItemTemplateSelector.
For more information about using and binding to a CompositeCollection, see this resource: How do you bind a CollectionContainer to a collection in a view model?
You could bind your ListBoxes to a Visibility variable. This way you can make the bottom box visible when you are in a situation where the bottom one would be selected and the top on collapsed and visa verse when you need the top one to be selected.
Related
I made a list view, and i used a data Template as you seeing
below and now i want to select the Which item,the user selected and put the text of the selected item textblock to a variable
<ListView.ItemTemplate>
<DataTemplate >
<Border CornerRadius="20"
BorderThickness="2"
BorderBrush="Red"
Width="70"
Height="70"
Margin="5,5">
<Button x:Name="calender_btn"
Background="Transparent"
BorderBrush="Transparent"
Style="{StaticResource calender_btn_style}"
Click="calender_btn_Click">
<TextBlock x:Name="calender_txt_block"
Text="{Binding _outputdate}"
Foreground="White"
Width="55"
FontSize="14"
Margin="3,5"
TextWrapping="Wrap"
VerticalAlignment="Center"
HorizontalAlignment="Center"
TextAlignment="Center"
>
</TextBlock>
<Button.Resources>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="20"/>
</Style>
</Button.Resources>
</Button>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
I have no idea how I can do it, because I am new in wpf, if you know how I can do it.
Depending on how your data looks like, you probably get a data object to each button that contains the text. You can find that object in the buttons data context.
private void calender_btn_Click(object sender, RoutedEventArgs e)
{
var dataObject = ((Button)sender).DataContext;
}
If you are using ItemsSource you can get the selected value by binding to ListView SelectedItem (SelectedValue in some cases).
SelectedItem="{Binding SelectedData, Mode=TwoWay}"
Assuming that you have SelectedData property in your DataContext with same type as your data in the shown collection.
Google for MVVM
I'm creating a WPF application where I have to bind a collection to a Canvas, then, each element is showed as an ellipse. So, since I want to be able to move all the elements on demand I bound each element's viewmodel to Canvas.Left and Canvas.Top. However, when I assign a new value to my viewmodel's bound properties the ellipse does not move.
My xaml file:
<ItemsControl ItemsSource="{Binding Elements}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Viewbox >
<Grid>
<Ellipse Stroke="Black"
Width="{Binding ShapeWidth, Mode=TwoWay}"
Height="{Binding ShapeHeight, Mode=TwoWay}"
Canvas.Left="{Binding ShapeCanvasLeft, Mode=TwoWay}"
Canvas.Top="{Binding ShapeCanvasTop, Mode=TwoWay}"
Fill="{Binding Background}"
/>
<TextBlock Text="{Binding ElementName}"
HorizontalAlignment="Center"
TextAlignment="Center"
VerticalAlignment="Center"
/>
</Grid>
</Viewbox>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
My viewmodel file:
public double ShapeCanvasLeft
{
get
{
return m_shapeCanvasLeft;
}
set
{
m_shapeCanvasLeft = value;
OnPropertyChanged(nameof(ShapeCanvasLeft));
}
}
public double ShapeCanvasTop
{
get
{
return m_shapeCanvasTop;
}
set
{
m_shapeCanvasTop = value;
OnPropertyChanged(nameof(ShapeCanvasTop));
}
}
. . .
Then in some other place:
ShapeCanvasTop = 100; // This does not work
So, what am I doing wrong? The shape does not move, however, the properties ShapeHeight and ShapeWidth work perfectly, i.e. the shape resizes correctly.
Thanks in advance.
ItemsControl items get wrapped in a parent container, that's what you need to be setting the position of:
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding ShapeCanvasLeft}" />
<Setter Property="Canvas.Top" Value="{Binding ShapeCanvasTop}" />
</Style>
</ItemsControl.ItemContainerStyle>
I have the following 2 DataTemplates:
<DataTemplate x:Key="ItemPhoto">
<StackPanel>
<Image Source="{Binding Thumbail}" Height="80" Width="80" HorizontalAlignment="Stretch" Stretch="UniformToFill"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="ItemAlbum">
<StackPanel>
<Image Source="{Binding Thumbail}" Height="100" Width="100" HorizontalAlignment="Stretch" Stretch="UniformToFill"/>
</StackPanel>
</DataTemplate>
And 1 GridView
<GridView Grid.Row="1" Name="lstFlickrControl"
SelectionMode="Multiple"
ItemTemplate="{StaticResource ItemPhoto}"
ItemClick="lstFlickrControl_ItemClick" Holding="lstFlickrControl_Holding">
<GridView.ItemContainerStyle>
<Style TargetType="FrameworkElement">
<Setter Property="Margin" Value="2"/>
</Style>
</GridView.ItemContainerStyle>
</GridView>
I want that when I click the photo menu GridView it should load the ItemPhoto DataTemple but when I click the albums menu GridView it should load the ItemAlbums DataTemple. Can someone please make some suggestions?
Try this way, for example upon albums menu clicked change ItemTemplate to use ItemAlbum DataTemplate :
lstFlickrControl.ItemTemplate = (DataTemplate)this.Resources["ItemAlbum"];
note : you may need to change this in above sample to whatever UI control where you define those DataTemplates
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>
I'm trying to create a control like this:
The control denotes the status completion of 4 tile objects (in the above attached pic 2 is completed and 2 is not)
For achieving this I have created a ListView with horizontal orientation, and I'm creating a datatemplate which has some borders inside.Not sure whether this is the right approach or whether a simpler approach is possible, however the issue I'm facing is the following:
I'm not able to close the border of the last item..this is because I have 4 items inside my itemsource of the listview and each item draws a border to it's left. Question is how do I specifically draw the right border for the last item.
The line which passess through the middle should actually go behind the gray shading..how do I achieve that?
Code is as below:
<Style x:Key="FishBoneStyle" TargetType="{x:Type Border}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Completed}" Value="True">
<Setter Property="Background" Value="DarkGray"/>
<Setter Property="Margin" Value="0,3,-2,3"/>
</DataTrigger>
</Style.Triggers>
</Style>
<DataTemplate x:Key="FishBoneTemplate">
<Grid Height="25">
<Border BorderThickness="1,0,0,0" BorderBrush="Black" Height="25" HorizontalAlignment="Left" VerticalAlignment="Stretch"/>
<Border Style="{StaticResource FishBoneStyle}" x:Name="FishBoneBorder" Width="25">
</Border>
</Grid>
</DataTemplate>
<!-- The Main Grid-->
<Grid Grid.Row="1" Height="30" Width="130" HorizontalAlignment="Left">
<ListView BorderThickness="0,0,0,0" BorderBrush="Black" ItemsSource="{Binding ProgressTiles}" ItemTemplate="{StaticResource FishBoneTemplate}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
<!-- The line which passess through the center-->
<Border VerticalAlignment="Center" HorizontalAlignment="Left" Width="100" Margin="3,0,2,0" BorderBrush="Black" BorderThickness="1"></Border>
</Grid>
..and here is the model which is lying inside an observable collection in my viewmodel.There will be always 4 of them.
public class ProgressTile : INotifyPropertyChanged
{
private bool _completed;
public bool Completed
{
get { return _completed; }
set
{
_completed = value;
InvokePropertyChanged("Completed");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void InvokePropertyChanged(string e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(e));
}
}
Please can you suggest how to get a look consistent to the attached picture. Also can you please suggest how to solve the issue with drawing a border for the last item and sending the line passing through the middle to background?
Can be done much simpler:
<Window x:Class="So17362172FishBoneProgressBar.MainWindow" x:Name="root"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Fish Bone Progress Bar" Height="350" Width="525" SnapsToDevicePixels="True">
<Grid Width="100" Height="20">
<TickBar Fill="Gray" TickFrequency="25" Placement="Top"/>
<TickBar Fill="Gray" TickFrequency="25" Placement="Bottom"/>
<Line Stroke="Gray" X1="0" Y1="10" X2="100" Y2="10"/>
<ItemsControl ItemsSource="{Binding Tiles, ElementName=root}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle x:Name="rect" Fill="Gray" VerticalAlignment="Center" Width="25" Height="10"/>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Completed}" Value="False">
<Setter TargetName="rect" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
ListView is unnecessary. Much simpler ItemsControl is enough for this.
Ticks can be drawn with TickBar. As inner ticks are drawn smaller, two tick bars are put over each other (it shouldn't be an issue, as these are very lightweight).
Order of drawing is determined by order in the logical tree. If you want something to be drawn over another, you put it into XAML lower.
Grid is unnecessary if it contains only one control.