I have a Window that has a UserControl. What i want to accomplish is Bind the ActualHeight of the StackPanel (stackpanel doesn't have a fixed size) that contains the UserControl in order to achive the correct behavior of the WrapPanel inside the UserControl.
I've tried Height="{Binding ElementName=MyStackPanel, Path=ActualHeight}" and Height="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type StackPanel}}, Path=ActualHeight}" but it didn't worked.
The way i place the UserControl inside the Window's StackPanel is as follows:
<StackPanel x:Name="MyStackPanel" Orientation="Vertical">
<!-- Wrap Panel / UserControl-->
<local:WrapControl />
</StackPanel>
If it helps the Main Window is a devexpress DXRibbonWindow.
Ok so it seems that StackPanel has no concept of "stretching" to occupy all the space available.
Replacing the StackPanel with a Grid works perfect.
Related
I have a TreeView that has 3 levels in it, driven by a HierarchicalDataTemplate. Here is a partial xaml listing:
<TreeView
Name="TreeView"
ItemsSource="{Binding GTOs}"
ScrollViewer.HorizontalScrollBarVisibility="Hidden">
<TreeView.ItemsPanel>
<ItemsPanelTemplate>
<!-- This binding prevents the TreeView from slipping over to the right when an item is clicked. -->
<StackPanel MaxWidth="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType=ContentPresenter, AncestorLevel=1}}" />
</ItemsPanelTemplate>
</TreeView.ItemsPanel>
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:TreeGTOViewModel}" ItemsSource="{Binding Children}">
<TextBlock
Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type TreeView}}, Path=ActualWidth}"
MouseDown="TextBlock_MouseDown"
Tag="{Binding Uri}"
Text="{Binding Title}" />
</HierarchicalDataTemplate>
The Binding on the Width attribute of the TextBlock insures that each TreeViewItem assumes the full width of the TreeView container (and not just the width of the text box). Coupled with the Material Design styling, this produces nice shading effects when the mouse is hovered over a particular item in the tree.
There's only one problem. While I can get the Width to assume the full width of the TreeViewItem, I can't seem to do the same for the Height. This means that the user can click near the edge of the TreeView item and see all of the effects as if that item was clicked, but the MouseDown event does not fire.
This is the area that should be clickable (the dotted red line):
This is the area that is actually clickable (the outside border of the Text Box):
I've tried a number of different approaches, including this one:
VerticalAlignment="Stretch"
and this one:
Height="{Binding RelativeSource={RelativeSource AncestorType={x:Type TreeViewItem}}, Path=ActualHeight}"
but they either have no effect, or they break the TreeView in unexpected ways. I think part of the problem is the usual height weirdness of the StackPanel that is being used in the ItemsPanelTemplate.
How do I get the TextBox in each TreeViewItem to assume the height of the actual TreeViewItem?
Below is an image of the relevant part of the visual tree, from the TreeView down to the clickable Text Box. The Ripple element is a MaterialDesignThemes.Wpf.Ripple object.
I made a very simple MahApp test project with a treeview and one treeviewitem (no binding).
In the Live Visual Tree, the PART_Header had a VerticalAlignment value of Center. If I changed it to Stretch (using Live Property Explorer), the textblock filled the space vertically.
So, I used the Document Outline in VS to edit a copy of the treeview template. Inside of that, I saw <Setter Property="VerticalContentAlignment" Value="Center"> so I decided to set it to Stretch on my TreeView and it seemed to work.
<TreeView x:Name="TreeView"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
VerticalContentAlignment="Stretch">
Well, I solved this by going a different route. Instead of hooking the MouseDown event on the Text Block, I'm going to hook the SelectedItemChanged event on the Tree View.
<TreeView
ItemsSource="{Binding GTOs}"
SelectedItemChanged="TreeView_SelectedItemChanged">
The RoutedPropertyChangedEventArgs of the SelectedItemChanged event contains the ViewModel for that particular TreeViewItem, which yields the Uri property I need for navigation (I was previously binding this property to the TextBox's Tag property).
The new Event Handler looks like this:
private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
dynamic viewModel = e.NewValue;
var uri = viewModel.Uri;
(DataContext as TreeViewModel).Navigate(uri);
}
Each level of the tree contains a different ViewModel type, but they all have a Uri property, so the dynamic binding provides the needed "don't care what type it is" behavior.
I can now remove the Tag binding and MouseDown event from the TextBlock.
<HierarchicalDataTemplate DataType="{x:Type local:TreeGTOViewModel}" ItemsSource="{Binding Children}">
<TextBlock
Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type TreeView}}, Path=ActualWidth}"
Text="{Binding Title}" />
and the Tree View items now respond properly, no matter where they are clicked.
I have a TextBlock having a canvas as its ToolTip. An user control is bounded to the canvas from the back-end in constructor. The TextBlock defined in xaml as follows:
<TextBlock Width="60" Height="40" Text="More info.">
<TextBlock.ToolTip>
<Canvas Name="canToolTip"></Canvas>
</TextBlock.ToolTip>
</TextBlock>
The constructor definition will be :
public UC_PublicationAdd()
{
InitializeComponent();
// assign datacontext
canToolTip.Children.Add(new ToolTipControl());
}
The whole scenario is fine Now let me come to the Problem. When i place the mouse over the TextBlock ToolTip is showing but its size is not controllable. What i mean is that, size of tooltip is too smaller(default size when tooltip text is not present). I want the tooltip equal to the size of the canvas
Sorry for Make you Confused, finally i solved the issue by assigning width and height to the canvas inside the tooltip. The xaml looks like the following:
<TextBlock Width="60" Height="40" Text="More info.">
<TextBlock.ToolTip>
<Canvas Name="canToolTip" Height="80" Width="130"></Canvas>
</TextBlock.ToolTip>
</TextBlock>
I'm interested in creating an app that displays some buttons and changes a viewport according to the selected button. The viewport in my app is a ContentControl and I thought of changing its content whenever a button is clicked. However, I believe there's a better approach, by perhaps injecting the ViewModels of each of the Views I want to present to the ContentControl and styling them using DataTemplates (Since I want to avoid having a grid with many controls and just setting their Visibility property whenever I want to show a particular view). Which of the approaches seems better to you? Do you have a different approach for this?
The view should be something similar to this:
Thanks!
Usually have a ViewModel behind the window which contains:
ObservableCollection<IViewModel> AvailableViewModels
IViewModel SelectedViewModel
ICommand SetCurrentViewModelCommand
I display the AvailableViewModels using an ItemsControl, which has its ItemTemplate set to a Button. The Button.Command is bound to the SetCurrentViewModelCommand, and it passes the current data item from the AvailableViewModels collection in through the CommandParameter
To display the content area, I use a ContentControl with ContentControl.Content bound to SelectedViewModel, and DataTemplates get used to tell WPF how to render each ViewModel.
The end result is my XAML looks something like this:
<Window.Resources>
<DataTemplate DataType="{x:Type local:ViewModelA}">
<local:ViewA />
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModelB}">
<local:ViewB />
</DataTemplate>
</Window.Resources>
<DockPanel>
<Border DockPanel.Dock="Left" BorderBrush="Black" BorderThickness="0,0,1,0">
<ItemsControl ItemsSource="{Binding AvailableViewModels}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}"
Command="{Binding DataContext.SetCurrentViewModelCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"
CommandParameter="{Binding }"
Margin="2,5"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
<ContentControl Content="{Binding SelectedViewModel}" />
</DockPanel>
You can view an example of the full code used for such a setup on my blog
I'm using an ItemsControl and a ItemTemplateSelector to draw display the UI of my items. But now, all my elements need to be inside in a Grid (and one of its columns the element should be there).
At the beginning I supposed was right to have a ContentControl nested in an ItemsControl, and this ContentControl should have the ItemTemplateSelector, but I'm not sure if this is the best way to do it.
Stuff like that should be in the Template of the item container, for ItemsControls that is a bit problematic as the containers are ContentPresenters which have no Template. You could subclass ItemsControl to use a ContentControl, then use the ItemsControl.ItemContainerStyle to edit the Template of those containers.
<ItemsControl x:Name="lst">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderThickness="10" CornerRadius="1" BorderBrush="Navy">
<TextBox Text="{Binding Name}"/>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I hope this will help.
My mouse wheel does not work when putting a ListBox in a ScrollViewer.
Does the ListBox somehow "steal" this event?
<ScrollViewer VerticalScrollBarVisibility="Auto" Style="{StaticResource myStyle}">
<ListBox>
<ListBoxItem>Test 1</ListBoxItem>
<ListBoxItem>Test 2</ListBoxItem>
<ListBoxItem>Test 3</ListBoxItem>
<ListBoxItem>Test 4</ListBoxItem>
<ListBoxItem>Test 5</ListBoxItem>
<ListBoxItem>Test 6</ListBoxItem>
<ListBoxItem>Test 7</ListBoxItem>
</ListBox>
</ScrollViewer>
Edit: as requested by Joel, added the reason why I did this..
I did this because I don't like what the ListBox's internal ScrollViewer does with my layout. I have a background image, and on top of that a ListBox as shown here:
alt text http://robbertdam.nl/share/1.png
Now when the scrollbar appears, the following happens:
alt text http://robbertdam.nl/share/2.png
I've created a Style for a ScrollViewer that shows the scroll bar on top of the ListBox item's content. In the ListBox item's datatemplate I've reserved some space for the scrollbar to appear.
Thanks,
Robbert Dam
Firstly, I think you need to elaborate on what your limitations are and what you're trying to achieve. Without that, I can only explain why what you're doing isn't working. Somebody may even have a better idea about how to get the result you're after.
If you put ListBox inside a ScrollViewer, then the control template for ListBox still has its own ScrollViewer inside. When the mouse cursor is over the ListBox and you scroll the mousewheel, that event bubbles up until it reaches the ScrollViewer that's part of ListBox. That one handles it by scrolling and marks the event as handled, so then the ScrollViewer you put the ListBox inside of ignores the event.
If you make the ListBox taller and narrower than the outer ScrollViewer, and give it enough items so that the ListBox itself can scroll the items, you'll see 2 vertical scroll bars: 1 in the ListBox, and 1 outside the ListBox for your outer ScrollViewer. When the mouse cursor is inside the ListBox, the ListBox will scroll the items with its internal ScrollViewer, and its Border will stay in place. When the mouse cursor is outside the ListBox and inside the outer ScrollViewer, that ScrollViewer will scroll its contents -- the ListBox -- which you can verify by noting that the ListBox's Border changes position.
If you want an outer ScrollViewer to scroll the entire ListBox control (including the Border and not just the items), you'll need to re-style the ListBox so that it does not have an internal ScrollViewer, but you'll also need to make sure it automatically gets bigger according to its items.
I don't recommend this approach for a couple reasons. It might make sense if there are other controls inside the ScrollViewer along with the ListBox, but your sample does not indicate that. Also, if you're going to have a lot of items in the ListBox, you'll be creating ListBoxItems for every single one, eliminating any advantage that the default, non-re-styled ListBox gives you due to the default VirtualizingStackPanel.
Please let us know what your actual requirements are.
Edit: Ok, now I have a little better idea, with the addition of those images. The effect you're getting is that when there are enough items to scroll and the scrollbar appears, the available area has to shrink a bit horizontally because the ScrollViewer's template uses a Grid. These seem to be your options, in order of lesser-to-better:
Re-style the ListBox to not have a ScrollViewer and use your re-styled ScrollViewer outside the ListBox. You'd then also have to force the ListBox to also be tall enough to show every item in that same Style, and now you've lost UI virtualization. If you're going to be showing hundreds of items in the list, you definitely don't want to lose that.
Re-style the ListBox and set the ControlTemplate to use a ScrollViewer with the style you already created for it that puts the scrollbar over the content rather than in a separate column. This one's ok (ListBox gets to limit its height and use a VirtualizingStackPanel, yay), but as you said, it necessitates awareness of that in your DataTemplate.
Re-style the ScrollViewer to leave space for vertical scrollbar even when it is not visible. Here's what this option looks like:
By default, ScrollViewer uses 2 columns in a Grid equivalent to this:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
So the Width of the scrollbar's column is 0 when the scrollbar is not visible since Width="Auto". To leave space for the scrollbar even when it is hidden, we bind the Width of that column to the Width of the vertical scroll bar:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition
Width="{Binding ElementName=PART_VerticalScrollBar, Path=Width}" />
</Grid.ColumnDefinitions>
So now the ControlTemplate in the custom Style for ScrollViewer might look like this:
<ControlTemplate
TargetType="{x:Type ScrollViewer}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition
Width="{Binding ElementName=PART_VerticalScrollBar, Path=Width}" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition
Height="Auto" />
</Grid.RowDefinitions>
<ScrollContentPresenter />
<ScrollBar
Grid.Column="1"
Name="PART_VerticalScrollBar"
Value="{TemplateBinding VerticalOffset}"
Maximum="{TemplateBinding ScrollableHeight}"
ViewportSize="{TemplateBinding ViewportHeight}"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" />
<ScrollBar
Name="PART_HorizontalScrollBar"
Orientation="Horizontal"
Grid.Row="1"
Value="{TemplateBinding HorizontalOffset}"
Maximum="{TemplateBinding ScrollableWidth}"
ViewportSize="{TemplateBinding ViewportWidth}"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" />
</Grid>
</ControlTemplate>
You could even make the content column a fixed size and the scrollbar column Width="*", which might work better in the long run if your image is not stretched. Now the DataTemplate does not have to compenstate for the width of a scrollbar, as it gets a consistent area to use whether the scrollbar is visible or not.
You'll probably want to check out the rest of the example ControlTemplate for ScrollViewer, but those examples are not the default styles. Note that the example puts the vertical scrollbar on the left! Also note the comment at the bottom about ContentScrollPresenter.
I was dealing with the same issue. I set the ListBox.ItemsPanel Property, without any ScrollViewer:
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<ListBox>
<ListBox.Template>
<ControlTemplate TargetType="ItemsControl">
<Border>
<ItemsPresenter />
</Border>
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<\ListBox>
<\ScrollViewer>
Hope this helps.
see this link for another answer:Scroll in ScrollViewer when mouse over ListBox
Hello, I think you can handle the whole ListBox’s PreviewMouseWheel event. This will also affect individual items as long as you don’t handle each item’s
PreviewMouseWheel and mark the event as handled:
private void ListBox_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
if (e.Delta < 0)
{
scrollViewer1.LineDown();
}
else
{
scrollViewer1.LineUp();
}
}
If you just want to make work the mouse scrolling and don't need a style for the ScrollViewer:
Remove the outer ScrollViewer.
Use the attached properties of the ScrollViewer, which is inside of the ListBox.
See first two lines of this example:
<ListBox ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto">
<ListBoxItem>Test 1</ListBoxItem>
<ListBoxItem>Test 2</ListBoxItem>
<ListBoxItem>Test 3</ListBoxItem>
<ListBoxItem>Test 4</ListBoxItem>
<ListBoxItem>Test 5</ListBoxItem>
<ListBoxItem>Test 6</ListBoxItem>
<ListBoxItem>Test 7</ListBoxItem>
</ListBox>