[英]Access XAML object in ViewModel
如何訪問 ViewModel 中的 XAML 對象? 我真的很困惑。 我想訪問<Controls:ModalContentPresenter>
對象。 我怎么能以符合 MVVM 的方式實現這一點? 在這個對象上我想調用一個方法ShowModalContent
<Controls:ModalContentPresenter x:Name="modalContent">
<ScrollViewer Behaviors:AdvancedZooming.KeepInCenter="true" Visibility="{Binding LeerformularIsVisible}" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Viewbox Stretch="Uniform">
<Grid>
<DataGrid BorderBrush="{x:Null}">
<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Command="{Binding AddFieldDefinitionCommand}" Header="Feld hinterlegen" Icon="pack://application:,,,/Images/Designer/field.png" />
<MenuItem Command="{Binding AddFunctionCommand}" Header="Funktion hinterlegen" Icon="pack://application:,,,/Images/Designer/FI_Taschenmesser_16x16.png" />
<MenuItem Command="{Binding RemoveFieldDefinitionCommand}" Header="Aktuelle Felddefinition entfernen" Icon="pack://application:,,,/Images/Designer/remove_field.png" />
<MenuItem Command="{Binding CutCommand}" Header="Ausschneiden" Icon="pack://application:,,,/Images/Zwischenablage/FI_Ausschneiden_16x16.png" />
<MenuItem Command="{Binding CopyCommand}" Header="Kopieren" Icon="pack://application:,,,/Images/Zwischenablage/FI_Kopieren_16x16.png" />
<MenuItem Command="{Binding PasteCommand}" Header="Einfügen" Icon="pack://application:,,,/Images/Zwischenablage/FI_Einfuegen_16x16.png" />
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>
</Grid>
</Viewbox>
</ScrollViewer>
<Controls:ModalContentPresenter.ModalContent>
<StackPanel>
<TextBlock>Test</TextBlock>
<Button>Hide</Button>
</StackPanel>
</Controls:ModalContentPresenter.ModalContent>
</Controls:ModalContentPresenter>
ModalContentPresenter的代碼可以在這里找到:
using System;
using System.Collections;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
namespace Controls
{
[ContentProperty("Content")]
public class ModalContentPresenter : FrameworkElement
{
#region private fields
private Panel layoutRoot;
private ContentPresenter primaryContentPresenter;
private ContentPresenter modalContentPresenter;
private Border overlay;
private object[] logicalChildren;
private KeyboardNavigationMode cachedKeyboardNavigationMode;
private static readonly TraversalRequest traversalDirection;
#endregion
#region dependency properties
public static readonly DependencyProperty IsModalProperty = DependencyProperty.Register("IsModal", typeof(bool), typeof(ModalContentPresenter),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnIsModalChanged));
public static readonly DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof(object), typeof(ModalContentPresenter),
new UIPropertyMetadata(null, OnContentChanged));
public static readonly DependencyProperty ModalContentProperty = DependencyProperty.Register("ModalContent", typeof(object), typeof(ModalContentPresenter),
new UIPropertyMetadata(null, OnModalContentChanged));
public static readonly DependencyProperty OverlayBrushProperty = DependencyProperty.Register("OverlayBrush", typeof(Brush), typeof(ModalContentPresenter),
new UIPropertyMetadata(new SolidColorBrush(Color.FromArgb(204, 169, 169, 169)), OnOverlayBrushChanged));
public bool IsModal
{
get { return (bool)GetValue(IsModalProperty); }
set { SetValue(IsModalProperty, value); }
}
public object Content
{
get { return (object)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
public object ModalContent
{
get { return (object)GetValue(ModalContentProperty); }
set { SetValue(ModalContentProperty, value); }
}
public Brush OverlayBrush
{
get { return (Brush)GetValue(OverlayBrushProperty); }
set { SetValue(OverlayBrushProperty, value); }
}
#endregion
#region routed events
public static readonly RoutedEvent PreviewModalContentShownEvent = EventManager.RegisterRoutedEvent("PreviewModalContentShown", RoutingStrategy.Tunnel,
typeof(RoutedEventArgs), typeof(ModalContentPresenter));
public static readonly RoutedEvent ModalContentShownEvent = EventManager.RegisterRoutedEvent("ModalContentShown", RoutingStrategy.Bubble,
typeof(RoutedEventArgs), typeof(ModalContentPresenter));
public static readonly RoutedEvent PreviewModalContentHiddenEvent = EventManager.RegisterRoutedEvent("PreviewModalContentHidden", RoutingStrategy.Tunnel,
typeof(RoutedEventArgs), typeof(ModalContentPresenter));
public static readonly RoutedEvent ModalContentHiddenEvent = EventManager.RegisterRoutedEvent("ModalContentHidden", RoutingStrategy.Bubble,
typeof(RoutedEventArgs), typeof(ModalContentPresenter));
public event RoutedEventHandler PreviewModalContentShown
{
add { AddHandler(PreviewModalContentShownEvent, value); }
remove { RemoveHandler(PreviewModalContentShownEvent, value); }
}
public event RoutedEventHandler ModalContentShown
{
add { AddHandler(ModalContentShownEvent, value); }
remove { RemoveHandler(ModalContentShownEvent, value); }
}
public event RoutedEventHandler PreviewModalContentHidden
{
add { AddHandler(PreviewModalContentHiddenEvent, value); }
remove { RemoveHandler(PreviewModalContentHiddenEvent, value); }
}
public event RoutedEventHandler ModalContentHidden
{
add { AddHandler(ModalContentHiddenEvent, value); }
remove { RemoveHandler(ModalContentHiddenEvent, value); }
}
#endregion
#region ModalContentPresenter implementation
static ModalContentPresenter()
{
traversalDirection = new TraversalRequest(FocusNavigationDirection.First);
}
public ModalContentPresenter()
{
layoutRoot = new ModalContentPresenterPanel();
primaryContentPresenter = new ContentPresenter();
modalContentPresenter = new ContentPresenter();
overlay = new Border();
AddVisualChild(layoutRoot);
logicalChildren = new object[2];
overlay.Background = OverlayBrush;
overlay.Child = modalContentPresenter;
overlay.Visibility = Visibility.Hidden;
layoutRoot.Children.Add(primaryContentPresenter);
layoutRoot.Children.Add(overlay);
}
public void ShowModalContent()
{
if (!IsModal)
IsModal = true;
}
public void HideModalContent()
{
if (IsModal)
IsModal = false;
}
private void RaiseModalContentShownEvents()
{
RoutedEventArgs args = new RoutedEventArgs(PreviewModalContentShownEvent);
OnPreviewModalContentShown(args);
if (!args.Handled)
{
args = new RoutedEventArgs(ModalContentShownEvent);
OnModalContentShown(args);
}
}
private void RaiseModalContentHiddenEvents()
{
RoutedEventArgs args = new RoutedEventArgs(PreviewModalContentHiddenEvent);
OnPreviewModalContentHidden(args);
if (!args.Handled)
{
args = new RoutedEventArgs(ModalContentHiddenEvent);
OnModalContentHidden(args);
}
}
protected virtual void OnPreviewModalContentShown(RoutedEventArgs e)
{
RaiseEvent(e);
}
protected virtual void OnModalContentShown(RoutedEventArgs e)
{
RaiseEvent(e);
}
protected virtual void OnPreviewModalContentHidden(RoutedEventArgs e)
{
RaiseEvent(e);
}
protected virtual void OnModalContentHidden(RoutedEventArgs e)
{
RaiseEvent(e);
}
#endregion
#region property changed callbacks
private static void OnIsModalChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ModalContentPresenter control = (ModalContentPresenter)d;
if ((bool)e.NewValue == true)
{
control.cachedKeyboardNavigationMode = KeyboardNavigation.GetTabNavigation(control.primaryContentPresenter);
KeyboardNavigation.SetTabNavigation(control.primaryContentPresenter, KeyboardNavigationMode.None);
control.overlay.Visibility = Visibility.Visible;
control.overlay.MoveFocus(traversalDirection);
control.RaiseModalContentShownEvents();
}
else
{
control.overlay.Visibility = Visibility.Hidden;
KeyboardNavigation.SetTabNavigation(control.primaryContentPresenter, control.cachedKeyboardNavigationMode);
control.primaryContentPresenter.MoveFocus(traversalDirection);
control.RaiseModalContentHiddenEvents();
}
}
private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ModalContentPresenter control = (ModalContentPresenter)d;
if (e.OldValue != null)
control.RemoveLogicalChild(e.OldValue);
control.primaryContentPresenter.Content = e.NewValue;
control.AddLogicalChild(e.NewValue);
control.logicalChildren[0] = e.NewValue;
}
private static void OnModalContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ModalContentPresenter control = (ModalContentPresenter)d;
if (e.OldValue != null)
control.RemoveLogicalChild(e.OldValue);
control.modalContentPresenter.Content = e.NewValue;
control.AddLogicalChild(e.NewValue);
control.logicalChildren[1] = e.NewValue;
}
private static void OnOverlayBrushChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ModalContentPresenter control = (ModalContentPresenter)d;
control.overlay.Background = (Brush)e.NewValue;
}
#endregion
#region FrameworkElement overrides
protected override Visual GetVisualChild(int index)
{
if (index < 0 || index > 1)
throw new ArgumentOutOfRangeException("index");
return layoutRoot;
}
protected override int VisualChildrenCount
{
get { return 1; }
}
protected override IEnumerator LogicalChildren
{
get { return logicalChildren.GetEnumerator(); }
}
protected override Size ArrangeOverride(Size finalSize)
{
layoutRoot.Arrange(new Rect(finalSize));
return finalSize;
}
protected override Size MeasureOverride(Size availableSize)
{
layoutRoot.Measure(availableSize);
return layoutRoot.DesiredSize;
}
#endregion
#region layout panel
class ModalContentPresenterPanel : Panel
{
protected override Size MeasureOverride(Size availableSize)
{
Size resultSize = new Size(0, 0);
foreach (UIElement child in Children)
{
child.Measure(availableSize);
resultSize.Width = Math.Max(resultSize.Width, child.DesiredSize.Width);
resultSize.Height = Math.Max(resultSize.Height, child.DesiredSize.Height);
}
return resultSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
foreach (UIElement child in InternalChildren)
{
child.Arrange(new Rect(finalSize));
}
return finalSize;
}
}
#endregion
}
}
您不應該這樣做(至少,這絕對不是 MVVM 方式)。
改用IsModal
屬性和數據綁定:
<Controls:ModalContentPresenter x:Name="modalContent" IsModal="{Binding IsModal}">
其中綁定表達式中的IsModal
是視圖模型中的綁定數據屬性。
在 MVVM 中,任何來回傳輸的數據都應該通過視圖模型上的數據綁定屬性來完成。 這幾乎包括ContentPresenter
所有屬性 - 只需添加一個綁定。 但是,如果您想以對 MVVM 友好的方式調用 XAML 對象的方法,則可以使用Action
。
假設我想通過視圖模型將焦點設置在文本框上(人為的示例,我知道還有其他 MVVM 方法可以做到這一點 - 只是想要一個需要視圖子對象的示例)。
首先,在 XAML 中為應該獲得焦點的文本框命名,即,
<TextBox x:Name="textBox1"/>
在您的視圖模型上,為 Action 添加一個屬性:
public Action FocusAction {get;set;}
在您的視圖加載之前或加載時,獲取您的 DataContext(即您的視圖模型)並將 Action 添加到后面的代碼(視圖的 .cs 文件):
ViewModel vm = (ViewModel)this.DataContext;
if ( vm.FocusAction == null )
vm.FocusAction= new Action(() => this.textBox1.Focus());
例如,您可以實現 IsLoaded 事件並在那里執行。 您也可以在 InitializeComponent 之后在構造函數中執行此操作,只要它創建了您的視圖模型實例或作為參數傳入。 關鍵是視圖模型已經實例化並分配給視圖的數據上下文。
然后在您的視圖模型中,無論您想將焦點添加到該文本框的何處,請調用:
FocusAction();
由於我剛剛在上面展示的內容要求您的視圖將 DataContext 轉換為特定的視圖模型,因此我所做的是為我需要的各種操作創建一個接口,如下所示:
interface IFocusable
{
Action FocusAction {get;set;}
}
然后我讓我的視圖模型實現那個接口。 完成后,在我看來的代碼隱藏中,我可以這樣說:
if(this.DataContext is IFocusable)
((IFocusable)this.DataContext).FocusAction = new Action(() => this.textBox1.Focus());
我認為這使得它更符合 MVVM,因為它沒有與特定的視圖模型緊密耦合,如果視圖模型是可以使用它的視圖模型類型,視圖只知道它可以添加一個動作。
此處提供更多詳細信息和另一個示例: http : //jkshay.com/closure-a-wpf-window-using-mvvm-and-minimal-code-behind/
您可以為接受 ContentPage 作為其參數的視圖模型創建一個構造函數。
public class ViewModelClass
{
public ViewModelClass(ContentPage p=null)
{...}
}
然后在 Contentpage 后台代碼腳本中設置綁定上下文,將 contentpage 的引用傳遞給 viewmodel
public class ContentPageClass : ContentPage
{
public ContentPageClass()
{
BindingContext = new ViewModelClass(p:this);
}
}
我知道已經有幾年了,但我只是面臨同樣的情況,所以這是我的答案,以防有人發現它有用......在您的 xaml 文件中,請參考 DataContext 中的 ViewModel 類:
<Window.DataContext>
<local:YourViewModel x:Name="yourViewModel"/>
</Window.DataContext>
在您的 ViewModel 類中,使用您的 xaml 文件作為參數和一個私有成員來創建一個公共函數來保存它:
private MainWindow mainWindow;
public void OnViewInitialized(MainWindow mainWindow)
{
this.mainWindow = mainWindow;
}
在您后面的代碼中,使用函數傳遞您在 xaml 中定義的“yourViewModel”:
public MainWindow()
{
InitializeComponent();
yourViewModel.OnViewInitialized(this);
}
就是這樣 :) 現在您可以使用 mainWindow 成員從 ViewModel 訪問所有 xaml 元素,只需為它們命名即可。
mainWindow.textBox1
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.