Wpf 如何在视图中创建控件的新实例而不违反MVVM
我遇到了与所描述的类似的问题。建议的解决方案是为我们希望呈现的每个now页面(PDF)创建一个新的WebBrowser控件(覆盖旧的WebBrowser控件)。Wpf 如何在视图中创建控件的新实例而不违反MVVM,wpf,silverlight,mvvm,Wpf,Silverlight,Mvvm,我遇到了与所描述的类似的问题。建议的解决方案是为我们希望呈现的每个now页面(PDF)创建一个新的WebBrowser控件(覆盖旧的WebBrowser控件)。 在MVVM中创建新控件的正确方法是什么?我试图让虚拟机不知道视图的实现。为什么虚拟机需要知道?为什么视图不能挂接到一个适当的事件(如果您愿意,可以定义一个,或者只使用属性changed)并重新创建控件 使用名为CreateBrowser()的方法在ViewModel中创建名为IBrowserCreator的接口 在ViewModel中创
在MVVM中创建新控件的正确方法是什么?我试图让虚拟机不知道视图的实现。为什么虚拟机需要知道?为什么视图不能挂接到一个适当的事件(如果您愿意,可以定义一个,或者只使用
属性changed
)并重新创建控件
ViewHelper.BrowserCreator.CreateBrowser()
显然,这个答案只是一个框架,但它应该给你一个大概的想法。您需要实现CreateBrowser方法来满足您的具体需求。为什么不简单地使用Datatemplate,让WPF来完成其余的工作
<UserControl x:Class="WpfBrowser.BrowserControl"
xmlns:WpfBrowser="clr-namespace:WpfBrowser" >
<Grid>
<WebBrowser WpfBrowser:WebBrowserUtility.BindableSource="{Binding MyPdf}"/>
</Grid>
</UserControl>
public class MyPageViewmodel: INotifyPropertyChanged
{
private MyPdfVM _myPdfStuff;
public MyPdfVM MyPdfStuff
{
get { return _myPdfStuff; }
set { _myPdfStuff = value; this.NotifyPropertyChanged(()=>this.MyPdfStuff);}
}
public MyViewmodel()
{
this.MyPdfStuff = new MyPdfVM();
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged<T>(Expression<Func<T>> property)
{
var propertyInfo = ((MemberExpression)property.Body).Member as PropertyInfo;
if (propertyInfo == null)
{
throw new ArgumentException("The lambda expression 'property' should point to a valid Property");
}
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyInfo.Name));
}
}
当您更改MyPdfStuff属性时,webbroswer将更新pdf
附属财产
public static class WebBrowserUtility
{
public static readonly DependencyProperty BindableSourceProperty =
DependencyProperty.RegisterAttached("BindableSource", typeof(string), typeof(WebBrowserUtility), new UIPropertyMetadata(null, BindableSourcePropertyChanged));
public static string GetBindableSource(DependencyObject obj)
{
return (string)obj.GetValue(BindableSourceProperty);
}
public static void SetBindableSource(DependencyObject obj, string value)
{
obj.SetValue(BindableSourceProperty, value);
}
public static void BindableSourcePropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
WebBrowser browser = o as WebBrowser;
if (browser != null)
{
string uri = e.NewValue as string;
browser.Source = string.IsNullOrWhiteSpace(uri) ? null:new Uri(uri);
}
}
}
编辑:添加一些代码,以便您可以看到,如果更改PDFViewmodel,您的浏览器控件将显示新的pdf。这不起作用。浏览器在更改URL时引发异常。请看我提到的另一个问题。它确实像一个咒语。我将添加一些代码,以便您可以尝试自己。谢谢!我真的很喜欢这个解决方案,但是我错过了Kent Boogaart建议的更简单的方法,这在我的例子中是可能的。我真的不喜欢这个解决方案中有一个静态属性。如果同时存在多个VM和V实例,该怎么办?在某些情况下,该解决方案可能是合适的,但我不建议这样做。Kent Boogaart的方法既简单又灵活。是的,这就是MVVM真正的意义所在。我想补充一点,证明这种方法的正确性。如果在某个时刻OP决定他/她需要在面板、窗口或其他任何地方显示内容,而不是在
WebBrowser
中,那么唯一需要更改的是视图中决定如何显示该内容的一小段代码。虚拟机仍然完好无损。
<Window x:Class="WpfBrowser.MainWindow"
xmlns:WpfBrowser="clr-namespace:WpfBrowser"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type WpfBrowser:MyPdfVM}">
<WpfBrowser:BrowserControl />
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="64*" />
<RowDefinition Height="247*" />
</Grid.RowDefinitions>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="32,14,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
<ContentControl Grid.Row="1" Content="{Binding MyPdfStuff}"/>
</Grid>
</Window>
public partial class MainWindow : Window
{
private MyViewmodel _data;
public MainWindow()
{
_data = new MyViewmodel();
InitializeComponent();
this.DataContext = _data;
}
private void button1_Click(object sender, RoutedEventArgs e)
{
this._data.MyPdfStuff = new MyPdfVM() { MyPdf = new Uri(@"your other pdf path for testing") };
}
}
public static class WebBrowserUtility
{
public static readonly DependencyProperty BindableSourceProperty =
DependencyProperty.RegisterAttached("BindableSource", typeof(string), typeof(WebBrowserUtility), new UIPropertyMetadata(null, BindableSourcePropertyChanged));
public static string GetBindableSource(DependencyObject obj)
{
return (string)obj.GetValue(BindableSourceProperty);
}
public static void SetBindableSource(DependencyObject obj, string value)
{
obj.SetValue(BindableSourceProperty, value);
}
public static void BindableSourcePropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
WebBrowser browser = o as WebBrowser;
if (browser != null)
{
string uri = e.NewValue as string;
browser.Source = string.IsNullOrWhiteSpace(uri) ? null:new Uri(uri);
}
}
}