按下按钮或键时如何从 AvalonDock 中的一个选项卡转到另一个选项卡?
How to go from one tab to another tab in AvalonDock when a button or key is pressed?
我从 SampleDockWindowView 创建了一个视图,我喜欢在按下按钮时转到创建的另一个视图 window。
我已经试过了Application.Current.Windows。这个数组只是空的。
window2 win2= new window2();
win2.Show();
我会想象这样的东西,但带有 avalondock 标签。不一定是新的 window,只是为了在按下按钮时显示现有的 window
以下示例显示了如何在按下 Ctrl + 右 组合键时循环浏览自定义 DocumentManager
的所有文档选项卡(在此示例中 DocumentManager
控件需要有焦点):
MainWindow.xaml
<Window>
<Window.DataContext>
<local:MainViewModel />
</Window.DataContext>
<Grid>
<!--
Border to capture keyboard events of the DocumentManager UserControl.
To capture keys in a different scope i.e. more globally,
move the input bindings to a parent control.
-->
<Border>
<!-- Bind keyboard keys to a ICommand. -->
<Border.InputBindings>
<KeyBinding Key="Right"
Modifiers="Control"
Command="{Binding NavigateToNextDocument}"/>
</Border.InputBindings>
<DocumentManager x:Name="DocumentManager" />
</Border>
</Grid
</Window>
DocumentManager.xaml
<UserControl>
<Grid>
<xceed:DockingManager DocumentsSource="{Binding DocumentMainPool}">
<xceed:DockingManager.LayoutItemTemplate>
<DataTemplate DataType="{x:Type local:Document}">
<TextBlock Text="{Binding Title}" />
</DataTemplate>
</xceed:DockingManager.LayoutItemTemplate>
<xceed:DockingManager.LayoutItemContainerStyle>
<Style TargetType="xcad:LayoutItem">
<Setter Property="Title"
Value="{Binding Model.Title}" />
<Setter Property="ToolTip"
Value="{Binding Model.Title}" />
<!-- Important -->
<Setter Property="IsSelected"
Value="{Binding Model.IsSelected, Mode=TwoWay}" />
</Style>
</xceed:DockingManager.LayoutItemContainerStyle>
<xceed:LayoutRoot>
<xceed:LayoutPanel>
<xceed:LayoutDocumentPaneGroup>
<!-- *** Dynamically created content (by view model) *** -->
<xceed:LayoutDocumentPane />
</xceed:LayoutDocumentPaneGroup>
</xceed:LayoutPanel>
</xceed:LayoutRoot>
</xceed:DockingManager>
</Grid>
</UserControl>
MainViewModel.cs
class MainViewModel : INotifyProeprtyChanged
{
public MainViewModel()
{
this.DocumentMainPool = new ObservableCollection<IDocument>()
{
new Document("First Document"),
new Document("Second Document")
};
}
private ObservableCollection<IDocument> documentMainPool;
public ObservableCollection<IDocument> DocumentMainPool
{
get => this.documentMainPool;
set
{
this.documentMainPool = value;
OnPropertyChanged();
}
}
public ICommand NavigateToNextDocument => new RelayCommand(param => CycleNextDocuments());
private void CycleNextDocuments()
{
// Only one or no document -> nothing to cycle through
if (this.DocumentMainPool.Count < 2)
{
return;
}
IDocument currentlySelectedDocument = this.DocumentMainPool.FirstOrDefault(document => document.IsSelected);
int currentDocumentIndex = this.DocumentMainPool.IndexOf(currentlySelectedDocument);
// If last document reached, show first again
if (currentDocumentIndex == this.DocumentMainPool.Count - 1)
{
this.DocumentMainPool.FirstOrDefault().IsSelected = true;
return;
}
IDocument nextDocument = this.DocumentMainPool
.Skip(currentDocumentIndex + 1)
.Take(1)
.FirstOrDefault();
nextDocument.IsSelected = true;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
IDocument.cs
// Important: IsSelected must raise PropertyChanged
public interface IDocument : INotifyPropertyChanged
{
string Title { get; set; }
bool IsSelected { get; set; }
}
Document.cs
public class Document : IDocument
{
public Document(string title)
{
this.Title = title;
}
#region Implementation of IDocument
public string Title { get; set; }
private bool isSelected;
public bool IsSelected
{
get => this.isSelected;
set
{
this.isSelected = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
RelayCommand.cs
实施取自 Microsoft Docs: Patterns - WPF Apps With The Model-View-ViewModel Design Pattern:
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
public RelayCommand(Action<object> execute) : this(execute, null) { }
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute; _canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter) { _execute(parameter); }
#endregion // ICommand Members
}
这里有一个 solution that works with MVVM 是有意义的,因为它是一个 MVVM 框架 :-)(我自己还没有尝试过,但是代码看起来很正常)。
我从 SampleDockWindowView 创建了一个视图,我喜欢在按下按钮时转到创建的另一个视图 window。
我已经试过了Application.Current.Windows。这个数组只是空的。
window2 win2= new window2();
win2.Show();
我会想象这样的东西,但带有 avalondock 标签。不一定是新的 window,只是为了在按下按钮时显示现有的 window
以下示例显示了如何在按下 Ctrl + 右 组合键时循环浏览自定义 DocumentManager
的所有文档选项卡(在此示例中 DocumentManager
控件需要有焦点):
MainWindow.xaml
<Window>
<Window.DataContext>
<local:MainViewModel />
</Window.DataContext>
<Grid>
<!--
Border to capture keyboard events of the DocumentManager UserControl.
To capture keys in a different scope i.e. more globally,
move the input bindings to a parent control.
-->
<Border>
<!-- Bind keyboard keys to a ICommand. -->
<Border.InputBindings>
<KeyBinding Key="Right"
Modifiers="Control"
Command="{Binding NavigateToNextDocument}"/>
</Border.InputBindings>
<DocumentManager x:Name="DocumentManager" />
</Border>
</Grid
</Window>
DocumentManager.xaml
<UserControl>
<Grid>
<xceed:DockingManager DocumentsSource="{Binding DocumentMainPool}">
<xceed:DockingManager.LayoutItemTemplate>
<DataTemplate DataType="{x:Type local:Document}">
<TextBlock Text="{Binding Title}" />
</DataTemplate>
</xceed:DockingManager.LayoutItemTemplate>
<xceed:DockingManager.LayoutItemContainerStyle>
<Style TargetType="xcad:LayoutItem">
<Setter Property="Title"
Value="{Binding Model.Title}" />
<Setter Property="ToolTip"
Value="{Binding Model.Title}" />
<!-- Important -->
<Setter Property="IsSelected"
Value="{Binding Model.IsSelected, Mode=TwoWay}" />
</Style>
</xceed:DockingManager.LayoutItemContainerStyle>
<xceed:LayoutRoot>
<xceed:LayoutPanel>
<xceed:LayoutDocumentPaneGroup>
<!-- *** Dynamically created content (by view model) *** -->
<xceed:LayoutDocumentPane />
</xceed:LayoutDocumentPaneGroup>
</xceed:LayoutPanel>
</xceed:LayoutRoot>
</xceed:DockingManager>
</Grid>
</UserControl>
MainViewModel.cs
class MainViewModel : INotifyProeprtyChanged
{
public MainViewModel()
{
this.DocumentMainPool = new ObservableCollection<IDocument>()
{
new Document("First Document"),
new Document("Second Document")
};
}
private ObservableCollection<IDocument> documentMainPool;
public ObservableCollection<IDocument> DocumentMainPool
{
get => this.documentMainPool;
set
{
this.documentMainPool = value;
OnPropertyChanged();
}
}
public ICommand NavigateToNextDocument => new RelayCommand(param => CycleNextDocuments());
private void CycleNextDocuments()
{
// Only one or no document -> nothing to cycle through
if (this.DocumentMainPool.Count < 2)
{
return;
}
IDocument currentlySelectedDocument = this.DocumentMainPool.FirstOrDefault(document => document.IsSelected);
int currentDocumentIndex = this.DocumentMainPool.IndexOf(currentlySelectedDocument);
// If last document reached, show first again
if (currentDocumentIndex == this.DocumentMainPool.Count - 1)
{
this.DocumentMainPool.FirstOrDefault().IsSelected = true;
return;
}
IDocument nextDocument = this.DocumentMainPool
.Skip(currentDocumentIndex + 1)
.Take(1)
.FirstOrDefault();
nextDocument.IsSelected = true;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
IDocument.cs
// Important: IsSelected must raise PropertyChanged
public interface IDocument : INotifyPropertyChanged
{
string Title { get; set; }
bool IsSelected { get; set; }
}
Document.cs
public class Document : IDocument
{
public Document(string title)
{
this.Title = title;
}
#region Implementation of IDocument
public string Title { get; set; }
private bool isSelected;
public bool IsSelected
{
get => this.isSelected;
set
{
this.isSelected = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
RelayCommand.cs
实施取自 Microsoft Docs: Patterns - WPF Apps With The Model-View-ViewModel Design Pattern:
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
public RelayCommand(Action<object> execute) : this(execute, null) { }
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute; _canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter) { _execute(parameter); }
#endregion // ICommand Members
}
这里有一个 solution that works with MVVM 是有意义的,因为它是一个 MVVM 框架 :-)(我自己还没有尝试过,但是代码看起来很正常)。