C# 使用GridSplitter作为扩展器,我这样做对吗?

C# 使用GridSplitter作为扩展器,我这样做对吗?,c#,wpf,xaml,C#,Wpf,Xaml,我正在尝试创建一个屏幕,屏幕右侧有隐藏的日志屏幕。我希望它在单击时展开,再次单击时隐藏。还希望它能够左右滑动,这样用户可以看到两个屏幕,如果愿意的话。还想调用函数从视图外部展开右屏幕。好的,到目前为止我所做的就是: 在XAML方面: <Grid x:Name="grdWorkArea" > <Grid.ColumnDefinitions> <ColumnDefinition Width="*" Name="Ma

我正在尝试创建一个屏幕,屏幕右侧有隐藏的日志屏幕。我希望它在单击时展开,再次单击时隐藏。还希望它能够左右滑动,这样用户可以看到两个屏幕,如果愿意的话。还想调用函数从视图外部展开右屏幕。好的,到目前为止我所做的就是:

在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>
<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>
所以这非常好,我可以手动移动拆分器或单击拆分器,然后左屏幕展开或隐藏。但是我需要能够从这个视图之外调用
GridSplitter\u PreviewMouseDown
。基本上在屏幕的顶部,我有一个包含按钮btnJournal的顶栏。当我点击它时,我希望日记本能够展开。因此,我决定将此函数移动到MainWindowViewModel,并将列宽绑定到属性:

在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>
<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>
在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;
    }
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);
    }


    // ...
}
所以当我点击spliiter时,它工作正常。当我移动拆分器时,它停止工作,并且不会对点击做出反应。事件将激发,但左侧屏幕不会展开。当我重新启动程序,它再次工作,直到我移动拆分器


我对Wpf和Xaml非常陌生,可能做得完全错误。有人能告诉我我做错了什么吗

我认为您的主要问题可能是绑定(我说可能,因为第一个解决方案对我来说也不完美,所以我猜您想要实现什么。这也使得我的解决方案与您的有点不同)。当绑定开始工作时,移动栅格滑块会覆盖宽度值,因此会丢失绑定。您可以尝试将TextBlock和TextBox绑定到按钮内的字符串,并将TextBlock的值设置为特定的值。在此之后,绑定将丢失

要想让它发挥作用,你有多种可能。一种方法是创建一个在ViewModel上工作的GridSplitter

另一种方法是将宽度设置为第一个解决方案中的宽度,并订阅该ViewModel的事件:

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;
    }
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方法


希望我正确理解了您的意图,这对您有所帮助。

我认为您的主要问题可能是绑定(我说可能,因为第一个解决方案对我来说也不太合适,所以我猜您想要实现什么。这也使我的解决方案与您的有点不同)。当绑定开始工作时,移动栅格滑块会覆盖宽度值,因此会丢失绑定。您可以尝试将TextBlock和TextBox绑定到按钮内的字符串,并将TextBlock的值设置为特定的值。在此之后,绑定将丢失

要想让它发挥作用,你有多种可能。一种方法是创建一个在ViewModel上工作的GridSplitter

另一种方法是将宽度设置为第一个解决方案中的宽度,并订阅该ViewModel的事件:

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;
    }
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
的方法,而不是。前者的问题是,根据相应属性上绑定集的模式,它的行为会有所不同-如果该模式支持目标到源方向(
TwoWay
OneWayToSource
模式),它会更新源,否则(
OneWay
OneTime
模式)它覆盖属性值源并将其设置为本地,从而有效地“破坏”绑定

现在,由于没有显式指定绑定模式,因此使用了默认模式(对于
ColumnDefinition.Width
属性为
OneWay
)。因此,一旦移动拆分器,它就会“断开”

为了使其按预期工作,应将绑定模式显式设置为双向:

<ColumnDefinition Width="{Binding Path=MainWindowWidth, Mode=TwoWay}"
                  Name="MainWindow"/>
<ColumnDefinition Width="8" />
<ColumnDefinition Width="{Binding Path=JournalWindowWidth, Mode=TwoWay}"
                  Name="JournalWindow"/>

这种行为最可能的原因是
网格拆分器的工作方式。如果我没有弄错的话,移动拆分器后,它会为相应的列设置
Width
值(或为行设置
Height
)。其关键部分是它使用了
DependencyObject
的方法,而不是。前者的问题是,根据相应属性上绑定集的模式,它的行为会有所不同-如果该模式支持目标到源方向(
TwoWay
OneWayToSource
模式),它会更新源,否则(
OneWay
OneTime
模式)它覆盖属性值源并将其设置为本地,从而有效地“破坏”绑定

现在,由于没有显式指定绑定模式,因此使用了默认模式(对于
ColumnDefinition.Width
属性为
OneWay
)。因此,一旦移动拆分器,它就会“断开”

为了让它像专家一样工作