简体   繁体   English

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

[英]Using GridSplitter as an expander, am I doing this right?

I'm trying to create aa screen with hidden Journal screen on right hand side. 我正在尝试创建一个右侧带有隐藏日记帐屏幕的屏幕。 I would like it to expand when clicked and hide when clicked again. 我希望它在单击时可以扩展,在再次单击时可以隐藏。 Also would like it to be able to slide it left and right so user can see both screens if want to. 还希望它能够左右滑动,以便用户可以同时看到两个屏幕。 Also would like to call the function to expand right screen from outside of the View. 还想调用该函数以从View外部扩展右屏幕。 Ok, so this what I have done so far: 好的,这是我到目前为止所做的:

on the XAML side: 在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>

on the View back end code: 在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;
        }
    }

So this works fantastic I can move splitter manually or click splitter and Left screen expands or hides. 因此,这很不错,我可以手动移动拆分器或单击拆分器,然后左屏幕展开或隐藏。 But I need to be able to call GridSplitter_PreviewMouseDown from outside of this View. 但是我需要能够从此View外部调用GridSplitter_PreviewMouseDown Basically on the top of the screen I have a TopBar which contains button btnJournal. 基本上在屏幕顶部,我有一个TopBar,其中包含btnJournal按钮。 When I click it I would like Journal to expand. 当我单击它时,我希望日记扩展。 So I decided to move this function to MainWindowViewModel and bind columns width to properties: 因此,我决定将此函数移动到MainWindowViewModel并将列的宽度绑定到属性:

on the XAML side: 在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>

And on the View back end code: 并在View后端代码上:

private void clickJournalExpand(object sender, MouseButtonEventArgs e)
    {
        var vm = (MainWindowViewModel)this.DataContext;
        vm.clkJournalExpand.Execute(null);
    }

In the MainWindowViewModel: 在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;
    }

So this works ok when I click spliiter. 因此,当我单击拆分器时,此方法正常。 When I move splitter it stops working and it not react to clicks as well. 当我移动拆分器时,它停止工作,并且对单击也没有反应。 The event fires but the Left screen doesn't expand. 事件触发,但“左”屏幕未展开。 When I restart program it works again until I move splitter. 当我重新启动程序时,它将再次起作用,直到我移动分离器为止。

I'm very new to Wpf and Xaml and probably doing this completely wrong. 我是Wpf和Xaml的新手,可能这样做完全错误。 Could some one give me direction what I'm doing wrong here? 有人可以指导我在这里做错什么吗?

I think your main problem might be the binding (I say might, because the first solution did not work perfectly for me neither, so I guessed what you are trying to achieve. Also this made my solution a bit different from yours). 我认为您的主要问题可能是绑定(我说可能是因为第一个解决方案对我而言也不完美,所以我猜到了您要实现的目标。这也使我的解决方案与您的解决方案有所不同)。 While your binding works at first, moving the grid slider overwrites the width values and so you're losing the binding. 绑定开始时,移动网格滑块会覆盖宽度值,因此您将丢失绑定。 You can try this with a TextBlock and TextBox binding to a string an inside a button set TextBlock's value to something specific. 您可以尝试通过将TextBlock和TextBox绑定到字符串内的按钮来尝试将按钮设置为特定值。 After this, the binding will be lost. 此后,绑定将丢失。

To get this to work you would have multiple possibilities. 为了使它起作用,您将有多种可能性。 One would be to create a GridSplitter which works on the ViewModel. 一种方法是创建一个可在ViewModel上使用的GridSplitter。

Another approach could be setting the Width as in your first solution and subscribe to an event of that ViewModel: 另一种方法是像在第一个解决方案中一样设置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);
    }


    // ...
}

The corresponding XAML: 相应的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>

You could also subscribe to PropertyChanged or you could implement an interface (which is known by the ViewModel) and change the expand method directly. 您也可以订阅PropertyChanged,也可以实现一个接口(ViewModel已知)并直接更改expand方法。

Hope I got your intention right and this helps you. 希望我的意图正确,对您有帮助。

Most likely cause of such behavior is the way the GridSplitter works. 此类行为最可能的原因是GridSplitter工作方式。 If I'm not mistaken, once you move the splitter, it sets Width values for corresponding columns (or Height for rows). 如果我没记错的话,一旦移动拆分器,它将为相应的列设置Width值(或为行设置Height )。 The key part of that is that it uses DependencyObject 's SetValue method, and not SetCurrentValue . 关键在于它使用了DependencyObjectSetValue方法,而不是SetCurrentValue The problem with the former is that it behaves differently depending on the mode of the binding set on the corresponding property - if that mode supports target-to-source direction ( TwoWay and OneWayToSource modes), it updates the source, otherwise ( OneWay and OneTime modes) it overwrites the property value source and sets it to local, effectively "breaking" the binding. 前者的问题在于,其行为取决于相应属性上绑定集的模式-如果该模式支持目标到源方向( TwoWayOneWayToSource模式),它将更新源,否则( OneWayOneTime模式),它会覆盖属性值源并将其设置为本地,从而有效地“打破”绑定。

Now since you don't explicitly specify the binding mode, the default is used (which is OneWay for ColumnDefinition.Width property). 现在,由于您没有明确指定绑定模式,因此将使用默认模式( ColumnDefinition.Width属性为OneWay )。 Hence it "breaks" once you move the splitter. 因此,一旦您移动分离器,它就会“破裂”。

In order to get it to work as expected it should be sufficient to explicitly set the binding modes to TwoWay : 为了使其按预期工作,将绑定模式显式设置为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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM