[英]Using GridSplitter as an expander, am I doing this right?
我正在尝试创建一个右侧带有隐藏日记帐屏幕的屏幕。 我希望它在单击时可以扩展,在再次单击时可以隐藏。 还希望它能够左右滑动,以便用户可以同时看到两个屏幕。 还想调用该函数以从View外部扩展右屏幕。 好的,这是我到目前为止所做的:
在XAML方面:
<Grid x:Name="grdWorkArea" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" Name="MainWindow"/>
<ColumnDefinition Width="8" />
<ColumnDefinition Width="0" Name="JournalWindow"/>
</Grid.ColumnDefinitions>
<ContentControl Name="ContentMain" Content="{Binding Path=CurrentViewModel}"/>
<Rectangle Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Fill="LightGray"/>
<GridSplitter PreviewMouseDown="GridSplitter_PreviewMouseDown" Grid.Column="1" Grid.Row="0" HorizontalAlignment="Stretch">
<GridSplitter.Style>
<Style TargetType="{x:Type GridSplitter}">
<Setter Property="Background">
<Setter.Value>
<ImageBrush Stretch="None" ImageSource="/Views;component/Images/ToggleWorkflow.png"/>
</Setter.Value>
</Setter>
</Style>
</GridSplitter.Style>
</GridSplitter>
<ContentControl Grid.Column="2" Name="ContentJournal" Content="{Binding Path=CurrentViewModel}"/>
</Grid>
在View后端代码上:
bool journalExpanded;
journalExpanded = false;
private void GridSplitter_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if(journalExpanded)
{
MainWindow.Width = new GridLength(1, GridUnitType.Star);
JournalWindow.Width = new GridLength(0);
journalExpanded = false;
}
else
{
MainWindow.Width = new GridLength(0);
JournalWindow.Width = new GridLength(1, GridUnitType.Star);
journalExpanded = true;
}
}
因此,这很不错,我可以手动移动拆分器或单击拆分器,然后左屏幕展开或隐藏。 但是我需要能够从此View外部调用GridSplitter_PreviewMouseDown
。 基本上在屏幕顶部,我有一个TopBar,其中包含btnJournal按钮。 当我单击它时,我希望日记扩展。 因此,我决定将此函数移动到MainWindowViewModel并将列的宽度绑定到属性:
在XAML方面:
<Grid x:Name="grdWorkArea" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding Path=MainWindowWidth}" Name="MainWindow"/>
<ColumnDefinition Width="8" />
<ColumnDefinition Width="{Binding Path=JournalWindowWidth}" Name="JournalWindow"/>
</Grid.ColumnDefinitions>
<ContentControl Grid.Column="0" Name="ContentMain" Content="{Binding Path=CurrentViewModel}"/>
<Rectangle Grid.Column="1" VerticalAlignment="Stretch" Fill="LightGray"/>
<GridSplitter PreviewMouseUp="clickJournalExpand" Grid.Column="1" Grid.Row="0" HorizontalAlignment="Stretch">
<GridSplitter.Style>
<Style TargetType="{x:Type GridSplitter}">
<Setter Property="Background">
<Setter.Value>
<ImageBrush Stretch="None" ImageSource="/Views;component/Images/ToggleWorkflow.png"/>
</Setter.Value>
</Setter>
</Style>
</GridSplitter.Style>
</GridSplitter>
<ContentControl Name="JournalMain" Content="{Binding Path=JournalVM}" Grid.Column="2" VerticalAlignment="Stretch" />
</Grid>
并在View后端代码上:
private void clickJournalExpand(object sender, MouseButtonEventArgs e)
{
var vm = (MainWindowViewModel)this.DataContext;
vm.clkJournalExpand.Execute(null);
}
在MainWindowViewModel中:
private string _MainWindowWidth = "*";
private string _JournalWindowWidth = "0";
public ICommand clkJournalExpand
{
get
{
return clickJournalExpand ?? (clickJournalExpand = new RelayCommand(() => ObeyclickJournalExpand()));
}
}
private void ObeyclickJournalExpand()
{
if (JournalExpanded)
{
MainWindowWidth = "*";
JournalWindowWidth = "0";
//JournalExpanded = false;
}
else
{
MainWindowWidth = "0";
JournalWindowWidth = "*";
//journalExpanded = true;
}
JournalExpanded = !JournalExpanded;
}
因此,当我单击拆分器时,此方法正常。 当我移动拆分器时,它停止工作,并且对单击也没有反应。 事件触发,但“左”屏幕未展开。 当我重新启动程序时,它将再次起作用,直到我移动分离器为止。
我是Wpf和Xaml的新手,可能这样做完全错误。 有人可以指导我在这里做错什么吗?
我认为您的主要问题可能是绑定(我说可能是因为第一个解决方案对我而言也不完美,所以我猜到了您要实现的目标。这也使我的解决方案与您的解决方案有所不同)。 绑定开始时,移动网格滑块会覆盖宽度值,因此您将丢失绑定。 您可以尝试通过将TextBlock和TextBox绑定到字符串内的按钮来尝试将按钮设置为特定值。 此后,绑定将丢失。
为了使它起作用,您将有多种可能性。 一种方法是创建一个可在ViewModel上使用的GridSplitter。
另一种方法是像在第一个解决方案中一样设置Width并订阅该ViewModel的事件:
public partial class MainWindow : Window
{
private double? _oldColumnJournalWindowWidth;
// it is expanded when Width > 0
public bool JournalExpanded
{
get { return ColumnJournalWindow.Width.Value > 0; }
}
public MainWindow()
{
InitializeComponent();
var viewModel = new MainWindowViewModel();
// Subscribe to event
viewModel.JournalExpandedChanged += ViewModel_JournalExpandedChanged;
DataContext = viewModel;
}
// When expanded set to either 100-0 or 50-50
private void ViewModel_JournalExpandedChanged(object sender, EventArgs e)
{
ColumnMainWindow.Width = new GridLength(1, GridUnitType.Star);
ColumnJournalWindow.Width = JournalExpanded ? new GridLength(0) : new GridLength(1, GridUnitType.Star);
}
// Handle expanding by click on splitter
private void GridSplitter_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
if (_oldColumnJournalWindowWidth == null)
return;
// if splitter was moved do not change expand
if (Math.Abs((double)_oldColumnJournalWindowWidth - ColumnJournalWindow.Width.Value) > 1)
return;
_oldColumnJournalWindowWidth = null;
ViewModel_JournalExpandedChanged(sender, e);
}
// Only on left button
private void GridSplitter_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.LeftButton != MouseButtonState.Pressed)
return;
_oldColumnJournalWindowWidth = ColumnJournalWindow.Width.Value;
}
}
internal class MainWindowViewModel : INotifyPropertyChanged
{
private ICommand _journalExpandCommand;
public ICommand JournalExpandCommand
{
get
{
return _journalExpandCommand ?? (_journalExpandCommand = new RelayCommand(OnJournalExpandedChanged));
}
}
// Event to trigger JournalExpanded should change
public event EventHandler JournalExpandedChanged;
protected virtual void OnJournalExpandedChanged()
{
if (JournalExpandedChanged != null)
JournalExpandedChanged(this, EventArgs.Empty);
}
// ...
}
相应的XAML:
<Grid x:Name="grdWorkArea" HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" Name="ColumnMainWindow"/>
<ColumnDefinition Width="8" />
<ColumnDefinition Width="*" Name="ColumnJournalWindow"/>
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="0" Name="ContentMain" Fill="#FFBD4444" Grid.Row="1"/>
<Rectangle Grid.Column="1" Grid.Row="1" VerticalAlignment="Stretch" Fill="LightGray" />
<GridSplitter PreviewMouseUp="GridSplitter_PreviewMouseUp" PreviewMouseDown="GridSplitter_PreviewMouseDown" Grid.Column="1" Grid.Row="1" HorizontalAlignment="Stretch">
<GridSplitter.Style>
<Style TargetType="{x:Type GridSplitter}">
<Setter Property="Background">
<Setter.Value>
<ImageBrush Stretch="None" ImageSource="image.png"/>
</Setter.Value>
</Setter>
</Style>
</GridSplitter.Style>
</GridSplitter>
<Rectangle Name="JournalMain" Grid.Column="2" VerticalAlignment="Stretch" Fill="#FF5E912A" Grid.Row="1" />
<Button Grid.Row="0" Grid.ColumnSpan="3" x:Name="button" Content="Expand" Command="{Binding JournalExpandCommand}" Height="20"/>
</Grid>
您也可以订阅PropertyChanged,也可以实现一个接口(ViewModel已知)并直接更改expand方法。
希望我的意图正确,对您有帮助。
此类行为最可能的原因是GridSplitter
工作方式。 如果我没记错的话,一旦移动拆分器,它将为相应的列设置Width
值(或为行设置Height
)。 关键在于它使用了DependencyObject
的SetValue
方法,而不是SetCurrentValue
。 前者的问题在于,其行为取决于相应属性上绑定集的模式-如果该模式支持目标到源方向( TwoWay
和OneWayToSource
模式),它将更新源,否则( OneWay
和OneTime
模式),它会覆盖属性值源并将其设置为本地,从而有效地“打破”绑定。
现在,由于您没有明确指定绑定模式,因此将使用默认模式( ColumnDefinition.Width
属性为OneWay
)。 因此,一旦您移动分离器,它就会“破裂”。
为了使其按预期工作,将绑定模式显式设置为TwoWay
应该足够了:
<ColumnDefinition Width="{Binding Path=MainWindowWidth, Mode=TwoWay}"
Name="MainWindow"/>
<ColumnDefinition Width="8" />
<ColumnDefinition Width="{Binding Path=JournalWindowWidth, Mode=TwoWay}"
Name="JournalWindow"/>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.