我想创建一个具有以下功能的多时间轴控件:
我最接近这个布局的是以下xaml:
<Grid>
<HeaderedContentControl>
<HeaderedContentControl.Template>
<ControlTemplate TargetType="HeaderedContentControl">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ContentPresenter ContentSource="Header" ContentTemplate="{TemplateBinding HeaderedContentControl.HeaderTemplate}" Content="{TemplateBinding HeaderedContentControl.Header}" />
<ScrollViewer Grid.Column="1" ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Content="{TemplateBinding ContentControl.Content}"
/>
</Grid>
</ControlTemplate>
</HeaderedContentControl.Template>
<HeaderedContentControl.Header>
<ItemsControl>
<Button Height="80" Content="Fixed Header 1" />
<Button Height="80" Content="Fixed Header 2" />
</ItemsControl>
</HeaderedContentControl.Header>
<ItemsControl>
<StackPanel Orientation="Horizontal" Height="80">
<Button Width="100" Content="thing" />
<Button Width="100" Content="thing" />
<Button Width="100" Content="thing" />
<Button Width="100" Content="thing" />
<Button Width="100" Content="thing" />
</StackPanel>
<StackPanel Orientation="Horizontal" Height="80">
<Button Width="100" Content="thing" />
<Button Width="100" Content="thing" />
<Button Width="100" Content="thing" />
<Button Width="100" Content="thing" />
<Button Width="100" Content="thing" />
</StackPanel>
</ItemsControl>
</HeaderedContentControl>
</Grid>
这为我提供了正确的水平滚动,但没有垂直滚动条:
所以为了获得垂直滚动,我只需要用ScrollViewer包装外部HeaderedContentControl元素,对吗?
<Grid>
<ScrollViewer>
<HeaderedContentControl>
<HeaderedContentControl.Template>
<ControlTemplate TargetType="HeaderedContentControl">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ContentPresenter
ContentSource="Header"
ContentTemplate="{TemplateBinding HeaderedContentControl.HeaderTemplate}"
Content="{TemplateBinding HeaderedContentControl.Header}" />
<ScrollViewer Grid.Column="1"
ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
VerticalScrollBarVisibility="Hidden"
HorizontalScrollBarVisibility="Auto"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Content="{TemplateBinding ContentControl.Content}" />
</Grid>
</ControlTemplate>
</HeaderedContentControl.Template>
<HeaderedContentControl.Header>
<ItemsControl>
<Button Height="80" Content="Fixed Header 1" />
<Button Height="80" Content="Fixed Header 2" />
</ItemsControl>
</HeaderedContentControl.Header>
<ItemsControl>
<StackPanel Orientation="Horizontal" Height="80">
<Button Width="100" Content="thing" />
<Button Width="100" Content="thing" />
<Button Width="100" Content="thing" />
<Button Width="100" Content="thing" />
<Button Width="100" Content="thing" />
</StackPanel>
<StackPanel Orientation="Horizontal" Height="80">
<Button Width="100" Content="thing" />
<Button Width="100" Content="thing" />
<Button Width="100" Content="thing" />
<Button Width="100" Content="thing" />
<Button Width="100" Content="thing" />
</StackPanel>
</ItemsControl>
</HeaderedContentControl>
</ScrollViewer>
</Grid>
结果:
嗯,它几乎是正确的,但是当我减小窗口高度时,我的时间轴下的水平滚动条会被修剪。我希望它像上一张图像一样保持在最顶层,这样我就可以一直水平滚动。
有谁知道我怎么做到这一点?
答案 0 :(得分:2)
我找到了一个解决方案(虽然感觉更像是一种解决方法,但你仍然是法官)。
我在内部ScrollViewer下添加了一个外部ScrollBar,并完全隐藏了内部ScrollBars。加载UserControl时,我找到属于ScrollViewer的水平ScrollBar,并将所有可视属性绑定到我的外部ScrollBar。当移动我的外部ScrollBar时,我使用ScrollToHorizontalOffset将值传递给内部ScrollViewer。我还添加了一个网格分割器和一些共享列宽,因此可以调整标题大小。
这是xaml:
<Grid Grid.IsSharedSizeScope="True">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="SharedHeader"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ScrollViewer Grid.ColumnSpan="2" VerticalScrollBarVisibility="Auto">
<HeaderedContentControl x:Name="HeaderedControl">
<HeaderedContentControl.Template>
<ControlTemplate TargetType="HeaderedContentControl">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="SharedHeader" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ContentPresenter
ContentSource="Header"
ContentTemplate="{TemplateBinding HeaderedContentControl.HeaderTemplate}"
Content="{TemplateBinding HeaderedContentControl.Header}" />
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Stretch" />
<ScrollViewer Grid.Column="2"
ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
VerticalScrollBarVisibility="Hidden"
HorizontalScrollBarVisibility="Hidden"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Content="{TemplateBinding ContentControl.Content}" />
</Grid>
</ControlTemplate>
</HeaderedContentControl.Template>
<HeaderedContentControl.Header>
<ItemsControl>
<Button Height="80" Content="Fixed Header 1" />
<Button Height="80" Content="Fixed Header 2" />
</ItemsControl>
</HeaderedContentControl.Header>
<ItemsControl>
<StackPanel Orientation="Horizontal" Height="80">
<Button Width="100" Content="thing" />
<Button Width="100" Content="thing" />
<Button Width="100" Content="thing" />
<Button Width="100" Content="thing" />
<Button Width="100" Content="thing" />
</StackPanel>
<StackPanel Orientation="Horizontal" Height="80">
<Button Width="100" Content="thing" />
<Button Width="100" Content="thing" />
<Button Width="100" Content="thing" />
<Button Width="100" Content="thing" />
<Button Width="100" Content="thing" />
</StackPanel>
</ItemsControl>
</HeaderedContentControl>
</ScrollViewer>
<ScrollBar Grid.Row="1" Grid.Column="1" x:Name="ExternalScroller" Orientation="Horizontal"/>
</Grid>
以下是代码隐藏:
public partial class MainWindow : Window
{
private ScrollViewer _internalScrollViewer;
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
// bind the external scrollbar to the internal ScrollViewer horizontal scrollbar
_internalScrollViewer = GetParentScrollViewer(HeaderedControl.Content as UIElement);
var hsb = GetHorizontalScrollbar(_internalScrollViewer);
BindingOperations.SetBinding(ExternalScroller, System.Windows.Controls.Primitives.RangeBase.LargeChangeProperty, new Binding("LargeChange") { Source = hsb, Mode = BindingMode.TwoWay });
BindingOperations.SetBinding(ExternalScroller, System.Windows.Controls.Primitives.RangeBase.MaximumProperty, new Binding("Maximum") { Source = hsb, Mode = BindingMode.TwoWay });
BindingOperations.SetBinding(ExternalScroller, System.Windows.Controls.Primitives.RangeBase.MinimumProperty, new Binding("Minimum") { Source = hsb, Mode = BindingMode.TwoWay });
BindingOperations.SetBinding(ExternalScroller, System.Windows.Controls.Primitives.RangeBase.SmallChangeProperty, new Binding("SmallChange") { Source = hsb, Mode = BindingMode.TwoWay });
BindingOperations.SetBinding(ExternalScroller, System.Windows.Controls.Primitives.RangeBase.ValueProperty, new Binding("Value") { Source = hsb, Mode = BindingMode.TwoWay });
BindingOperations.SetBinding(ExternalScroller, System.Windows.Controls.Primitives.ScrollBar.ViewportSizeProperty, new Binding("ViewportSize") { Source = hsb, Mode = BindingMode.TwoWay });
// forward change events to the internal ScrollViewer
ExternalScroller.ValueChanged += ExternalScroller_ValueChanged;
ExternalScroller.Value = hsb.Value;
}
private void ExternalScroller_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
_internalScrollViewer.ScrollToHorizontalOffset(e.NewValue);
}
private static System.Windows.Controls.Primitives.ScrollBar GetHorizontalScrollbar(ScrollViewer sv)
{
return FindChild<System.Windows.Controls.Primitives.ScrollBar>(sv, (sb => sb.Orientation == Orientation.Horizontal));
}
private static ScrollViewer GetParentScrollViewer(UIElement uiElement)
{
DependencyObject item = VisualTreeHelper.GetParent(uiElement);
while (item != null)
{
string name = "";
var ctrl = item as Control;
if (ctrl != null)
name = ctrl.Name;
if (item is ScrollViewer)
{
return item as ScrollViewer;
}
item = VisualTreeHelper.GetParent(item);
}
return null;
}
private static T FindChild<T>(DependencyObject parent, Func<T, bool> additionalCheck) where T : DependencyObject
{
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
T child;
for (int index = 0; index < childrenCount; index++)
{
child = VisualTreeHelper.GetChild(parent, index) as T;
if (child != null)
{
if (additionalCheck == null)
{
return child;
}
else
{
if (additionalCheck(child))
{
return child;
}
}
}
}
for (int index = 0; index < childrenCount; index++)
{
child = FindChild(VisualTreeHelper.GetChild(parent, index), additionalCheck);
if (child != null)
{
return child;
}
}
return null;
}
}
最后,这是视觉效果: