【WPF】MVVM是如何解耦的

前言

        因为本身对wpf理解不深如果有技术方面的问题希望可以帮忙纠正,谢谢。

1.MVVM是什么

        从百度百科(MVVM_百度百科)可以了解它出现的原因以及优点。它的其中一个优点就是低耦合,这篇文章也是想聊聊.net是如何对Model-View解耦的,因为.net的代码比较复杂,现在只拆出来关键代码来描述。

 2.Wpf中的Binding

        熟悉wpf的朋友都知道wpf中大部分控件都继承自FrameworkElement,FrameworkElement中有一个方法SetBinding

public BindingExpressionBase SetBinding(DependencyProperty dp, BindingBase binding)
{
    return BindingOperations.SetBinding(this, dp, binding);
}

        这个方法有两个参数DependencyProperty和BindingBase以及一个返回值BindingExpressionBase,我们简单看下这3个类

        (1)DependencyProperty wpf中的依赖属性 例如:TextBox.TextProperty

public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(TextBox), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal, OnTextPropertyChanged, CoerceText, isAnimationProhibited: true, UpdateSourceTrigger.LostFocus));

public string Text
{
	get
	{
		return (string)GetValue(TextProperty);
	}
	set
	{
		SetValue(TextProperty, value);
	}
}

        (2)BindingBase wpf中数据绑定的基类,经常使用的Binding就是BindingBase的子类,BindingBase中有一个内部的方法CreateBindingExpression,CreateBindingExpression中直接调用了抽象方法CreateBindingExpressionOverride,主要代码如下

internal BindingExpressionBase CreateBindingExpression(DependencyObject targetObject, DependencyProperty targetProperty)
{
	_isSealed = true;
	return CreateBindingExpressionOverride(targetObject, targetProperty, null);
}

internal abstract BindingExpressionBase CreateBindingExpressionOverride(DependencyObject targetObject, DependencyProperty targetProperty, BindingExpressionBase owner);

        Binding继承BindingBase重写了CreateBindingExpressionOverride

internal override BindingExpressionBase CreateBindingExpressionOverride(DependencyObject target, DependencyProperty dp, BindingExpressionBase owner)
{
	return BindingExpression.CreateBindingExpression(target, dp, this, owner);
}

        可以看到CreateBindingExpressionOverride的返回值为BindingExpressionBase,等下再说这两个方法,先介绍BindingExpressionBase

        (3)BindingExpressionBase 从字面意思可以理解为Binding的表达式,实际上就是存储Binding的值的关系(谁绑定了谁)

        可以看出SetBinding时将控件的依赖属性和要Binding的数据做了一个关系,即BindingExpressionBase,我们知道一个正常的通知要么用委托要么用事件将两个需要相互影响的值关联起来。下面我列举2种形式:

        (1)通知方/被通知方关联目标对象

public class TestVM
{
	public event Action<string> VMChanged;

	private string vM;

	public string VM
	{
		get => vM;
		set
		{
			vM = value;
			VMChanged?.Invoke(vM);
		}
	}
}

public class MyButton : Button
{
    public MyButton()
    { 
            
    }

	public MyButton(TestVM testVM)
	{
		testVM.VMChanged += TestVM_VMChanged;
	}

	private void TestVM_VMChanged(string data)
	{
		this.Content = data;
	}
}

        如上方代码所示,可以在通知方增加属性改变事件,在被通知方构造时订阅这个改变事件从而实现值改变,但是这样做会有一个明显的缺点TestVM和MyButton出现和耦合,MyButton需要有一个方法将TestVM传递到类内部,此时MyButton订阅了TestVM的VMChanged事件,那么假如TestVM一直没被回收,MyButton就一直有一个TestVM的引用导致MyButton无法被回收

        (2)借助第3个类

public class Main
{
	private MyButton _testButton;

	private TestVM _testVM;

	public void Init()
	{
		_testButton = new MyButton();
		_testVM = new TestVM();
		_testVM.VMChanged += TestVM_VMChanged;
	}

	private void TestVM_VMChanged(string data)
	{
		if (_testButton != null)
			_testButton.Content = data;
	}

	public void DisposedTestButton()
	{
		_testButton = null;
	}

	public void DisposedTestTestVM()
	{
		_testVM = null;
	}
}

        如上方代码所示,借助第3个类Main来将TestVM和MyButton关联起来,这样的话TestVM和MyButton直接就没有了直接联系,我们单方面去调用DisposedTestButton或者DisposedTestTestVM都是可以将他们回收掉的,这也是我们日常开发所用到最多的处理方式,将两个需要相互或者单方向通知的对象之间的关系转移到第3个类(主页面),当然Binding也是采用这种处理方式,不过由于Binding需要更通用且需要自动回收所以做了一些额外处理

 3.INotifyPropertyChanged

        下面时INotifyPropertyChanged接口的源码

public interface INotifyPropertyChanged
{
	event PropertyChangedEventHandler PropertyChanged;
}

        我们日常的用法和MvvmLight中的ObservableObject的用法是一致的,下面贴出ObservableObject的部分源码以及我们的日常实现

public class ObservableObject : INotifyPropertyChanged
{
	public event PropertyChangedEventHandler PropertyChanged;

	public virtual void RaisePropertyChanged([CallerMemberName] string propertyName = null)
	{
		this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
	}
}


public class TestVM : INotifyPropertyChanged
{
	public event Action<string> VMChanged;

	private string vM;

	public string VM
	{
		get => vM;
		set
		{
			vM = value;
			VMChanged?.Invoke(vM);
			NotifyPropertyChanged("VM");
		}
	}

	#region 实现接口

	public event PropertyChangedEventHandler PropertyChanged;

	public void NotifyPropertyChanged(string propertyName)
	{
		PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
	}

	#endregion
}

        当然只是这样还不够,还需要在前台xaml中对他们进行绑定,在将MyButton_Test的上下文设置为指定的对象(TestVM),就可以实现TestVM和MyButton的相互通知了

<local:MyButton x:Name="MyButton_Test" Content="{Binding VM}"/>

        不过我们知道两个事物不可能无缘无故的联系起来,那么必定在Binding的时候做了一些处理,将两个对象关联起来了,下面看源码,由于源码较长只截取关键路径

public sealed class BindingExpression : BindingExpressionBase, IDataBindEngineClient, IWeakEventListener
{
	private BindingWorker Worker => _worker;
	
	private void CreateWorker()
	{
		Invariant.Assert(Worker == null, "duplicate call to CreateWorker");
		_worker = new ClrBindingWorker(this, base.Engine);
	}
	
	public override void UpdateTarget()
	{
		if (base.IsDetached)
		{
			throw new InvalidOperationException(SR.Get("BindingExpressionIsDetached"));
		}

		if (Worker != null)
		{
			Worker.RefreshValue();
		}
	}
}

internal class ClrBindingWorker : BindingWorker
{
	private PropertyPathWorker PW => _pathWorker;
	
	internal override void RefreshValue()
	{
		PW.RefreshValue();
	}
}

internal sealed class PropertyPathWorker : IWeakEventListener
{
	internal void RefreshValue()
	{
		for (int i = 1; i < _arySVS.Length; i++)
		{
			object reference = BindingExpressionBase.GetReference(_arySVS[i].item);
			if (!ItemsControl.EqualsEx(reference, RawValue(i - 1)))
			{
				UpdateSourceValueState(i - 1, null);
				return;
			}
		}

		UpdateSourceValueState(Length - 1, null);
	}
	
	private void UpdateSourceValueState(int k, ICollectionView collectionView)
	{
		UpdateSourceValueState(k, collectionView, BindingExpression.NullDataItem, isASubPropertyChange: false);
	}

	private void UpdateSourceValueState(int k, ICollectionView collectionView, object newValue, bool isASubPropertyChange)
	{
		//...省略前方代码
		for (k++; k < _arySVS.Length; k++)
		{
			isASubPropertyChange = false;
			ICollectionView collectionView2 = _arySVS[k].collectionView;
			obj = ((newValue == BindingExpression.NullDataItem) ? RawValue(k - 1) : newValue);
			newValue = BindingExpression.NullDataItem;
			if (obj == AsyncRequestPending)
			{
				_status = PropertyPathStatus.AsyncRequestPending;
				break;
			}

			if (!flag && obj == BindingExpressionBase.DisconnectedItem && _arySVS[k - 1].info == FrameworkElement.DataContextProperty)
			{
				flag = true;
			}

			ReplaceItem(k, BindingExpression.NullDataItem, obj);
			ICollectionView collectionView3 = _arySVS[k].collectionView;
			if (collectionView2 != collectionView3 && _host != null)
			{
				_host.ReplaceCurrentItem(collectionView2, collectionView3);
			}
		}
		//...省略后方代码
	}

	private void ReplaceItem(int k, object newO, object parent)
	{
		//...省略前方代码
		if (IsDynamic && SVI[k].type != SourceValueType.Direct)
		{
			Engine.RegisterForCacheChanges(newO, svs.info);
			PropertyPath.DowncastAccessor(svs.info, out DependencyProperty dp2, out PropertyInfo pi2, out PropertyDescriptor pd2, out DynamicObjectAccessor _);
			INotifyPropertyChanged source2;
			if (newO == BindingExpression.StaticSource)
			{
				Type type2 = (pi2 != null) ? pi2.DeclaringType : pd2?.ComponentType;
				if (type2 != null)
				{
					StaticPropertyChangedEventManager.AddHandler(type2, OnStaticPropertyChanged, SVI[k].propertyName);
				}
			}
			else if (dp2 != null)
			{
				_dependencySourcesChanged = true;
			}
			else if ((source2 = (newO as INotifyPropertyChanged)) != null)
			{
				PropertyChangedEventManager.AddHandler(source2, OnPropertyChanged, SVI[k].propertyName);
			}
			else if (pd2 != null && newO != null)
			{
				ValueChangedEventManager.AddHandler(newO, OnValueChanged, pd2);
			}
		}
		//...省略后方代码
	}
}

public class PropertyChangedEventManager : WeakEventManager
{
	protected override void StartListening(object source)
	{
		INotifyPropertyChanged notifyPropertyChanged = (INotifyPropertyChanged)source;
		notifyPropertyChanged.PropertyChanged += OnPropertyChanged;
	}
	
	protected override void StopListening(object source)
	{
		INotifyPropertyChanged notifyPropertyChanged = (INotifyPropertyChanged)source;
		notifyPropertyChanged.PropertyChanged -= OnPropertyChanged;
	}
	
	public static void AddHandler(INotifyPropertyChanged source, EventHandler<PropertyChangedEventArgs> handler, string propertyName)
	{
		if (handler == null)
		{
			throw new ArgumentNullException("handler");
		}

		CurrentManager.PrivateAddHandler(source, handler, propertyName);
	}
		
	private void PrivateAddHandler(INotifyPropertyChanged source, EventHandler<PropertyChangedEventArgs> handler, string propertyName)
	{
		AddListener(source, propertyName, null, handler);
	}
	
	private void AddListener(INotifyPropertyChanged source, string propertyName, IWeakEventListener listener, EventHandler<PropertyChangedEventArgs> handler)
	{
		using (base.WriteLock)
		{
			//...省略前方代码
			HybridDictionary hybridDictionary = (HybridDictionary)base[source];
			if (hybridDictionary == null)
			{
				hybridDictionary = (HybridDictionary)(base[source] = new HybridDictionary(caseInsensitive: true));
				StartListening(source);
			}
            //...省略后方代码
		}
	}

	private void OnPropertyChanged(object sender, PropertyChangedEventArgs args)
	{
		//...省略前方代码
		base.DeliverEventToList(sender, args, listenerList);
		//...省略后方代码
	}
}

public abstract class WeakEventManager : DispatcherObject
{
	protected void DeliverEventToList(object sender, EventArgs args, WeakEventManager.ListenerList list)
	{
		bool flag = list.DeliverEvent(sender, args, base.GetType());
		if (flag)
		{
			this.ScheduleCleanup();
		}
	}		

	public override bool DeliverEvent(object sender, EventArgs e, Type managerType)
	{
		TEventArgs e2 = (TEventArgs)((object)e);
		bool flag = false;
		int i = 0;
		int count = base.Count;
		while (i < count)
		{
			WeakEventManager.Listener listener = base.GetListener(i);
			if (listener.Target != null)
			{
				//eventHandler就是PropertyPathWorker中订阅INotifyPropertyChanged.PropertyChanged事件的方法
				EventHandler<TEventArgs> eventHandler = (EventHandler<TEventArgs>)listener.Handler;
				if (eventHandler != null)
				{
					eventHandler(sender, e2);
				}
				else
				{
					flag |= base.DeliverEvent(ref listener, sender, e, managerType);
				}
			}
			else
			{
				flag = true;
			}
			i++;
		}
		return flag;
	}
}

        可以看到在BindingExpression在调用UpdateTarget时最终会调用PropertyChangedEventManager的StartListening订阅INotifyPropertyChanged的PropertyChanged事件,至此两个绑定的属性产生了联系,只要我们在模型里面调用PropertyChanged就可是通知到对应需要改变的对象的属性,需要注意的是这里分别有一个重要的类和接口(WeakEventManager和IWeakEventListener),我们可以看到WeakEventManager充当的角色就是上面的Main(第三个类)将两个类关系起来,下面我们实现自己的通知时需要用到,由于具体改变属性的代码量比较大并且控件和UI具体怎么交互也不太了解,之后学习DependencyObject和DependencyProperty再详细介绍

        下面是简单的一个调用关系图

 4.根据.Net的实现逻辑实现通知

       说了这么多我们仿照着.net的实现逻辑自己实现一份,下面先贴代码

public interface IMyWeakEventTest
{
	event Action<object, string> PropertyChanged;
}

        第一个接口IMyWeakEventTest对应INotifyPropertyChanged

public class MyWeakEventBindingExpTest : IWeakEventListener
{
	public MyWeakEventBindingExpTest(DependencyObject dobj, DependencyProperty dp, object obj, string sourceFiled)
	{
		Target = new WeakReference<DependencyObject>(dobj);
		Source = new WeakReference<object>(obj);
		DProperty = dp;
		SourceFiled = sourceFiled;
	}

	public DependencyProperty DProperty { get; set; }

	public WeakReference<DependencyObject> Target { get; set; }

	public WeakReference<object> Source { get; set; }

	public string SourceFiled { get; set; }

	public object RSource
	{
		get
		{
			if (Source.TryGetTarget(out var source))
				return source;
			return null;
		}
	}

	public DependencyObject RTarget
	{
		get
		{
			if (Target.TryGetTarget(out var target))
				return (DependencyObject)target;
			return null;
		}
	}

	public void UpdateTarget()
	{
		this.RTarget.SetValue(this.DProperty, this.RSource.GetType().GetProperty(SourceFiled)?.GetValue(this.RSource));
	}

	public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
	{
		var myWeakEventArgsTest = e as MyWeakEventArgsTest;
		if (myWeakEventArgsTest == null)
			return true;

		if (myWeakEventArgsTest.Value != SourceFiled)
			return true;

		this.UpdateTarget();
		return true;
	}
}

        第二个MyWeakEventBindingExpTest对应BindingExpression和PropertyPathWorker,由于现在只说原理所以将BindingExpression和PropertyPathWorker的功能合并了,这里可以看到Source(数据源)和Target(目标)我们用了WeakReference(弱引用类)目的是为了内存的自动释放

public class MyWeakEventManagerTest : WeakEventManager
{
	protected override void StartListening(object source)
	{
		var weTest = source as IMyWeakEventTest;
		if (weTest != null)
			weTest.PropertyChanged += WeTest_PropertyChanged;
	}

	private void WeTest_PropertyChanged(object arg1, string arg2)
	{
		base.DeliverEvent(arg1, new MyWeakEventArgsTest() { Value = arg2 });
	}

	protected override void StopListening(object source)
	{
		var weTest = source as IMyWeakEventTest;
		if (weTest != null)
			weTest.PropertyChanged -= WeTest_PropertyChanged;
	}

	public void AddLinstener(object source, IWeakEventListener eventListener)
	{
		var myBindingExp = eventListener as MyWeakEventBindingExpTest;
		MyWeakEventTestExtension.SetWeakListener(myBindingExp.RTarget, myBindingExp);
		myBindingExp.UpdateTarget();
		this.ProtectedAddListener(source, eventListener);
	}

	public void RemoveLinstener(object source, IWeakEventListener eventListener)
	{
		this.ProtectedRemoveListener(source, eventListener);
	}
}

        第三个MyWeakEventManagerTest对应PropertyChangedEventManager

public class MyWeakEventTestExtension
{
	public static readonly DependencyProperty WeakListenerProperty = DependencyProperty.RegisterAttached("WeakListener", typeof(IWeakEventListener), typeof(MyWeakEventTestExtension), new PropertyMetadata(null));

	public static void SetWeakListener(DependencyObject element, IWeakEventListener value) => element.SetValue(WeakListenerProperty, value);

	public static IWeakEventListener GetWeakListener(DependencyObject element) => (IWeakEventListener)element.GetValue(WeakListenerProperty);
}

        第四个是一个扩展类,主要是为了在Target(即控件)存储IWeakEventListener即MyWeakEventBindingExpTest避免MyWeakEventBindingExpTest被回收

        上面代码和框架提供的结构基本一致同样用到了WeakEventManager和IWeakEventListene先看类名WeakEvent(弱事件)Manager(管理员),我们都知道弱引用它会在系统需要内存时被回收,WeakEventManager也是同样的,WeakEventManager内部有一个虚方法Purge(清除),会对无用的订阅进行释放,IWeakEventListene就是为WeakEventManager服务的

protected virtual bool Purge(object source, object data, bool purgeAll)
{
	bool result = false;
	bool flag = purgeAll || source == null;
	if (!flag)
	{
		ListenerList list = (ListenerList)data;
		if (ListenerList.PrepareForWriting(ref list) && source != null)
		{
			Table[this, source] = list;
		}

		if (list.Purge())
		{
			result = true;
		}

		flag = list.IsEmpty;
	}

	if (flag && source != null)
	{
		StopListening(source);
		if (!purgeAll)
		{
			Table.Remove(this, source);
			result = true;
		}
	}

	return result;
}

 5.测试自定义通知,内存分析

        我们先简单的搭一个测试例子

         后台代码如下

public partial class Window13 : Window
{
	#region 私有成员

	//索引
	private int _index = 1;

	//通知模型列表
	private List<MyWeakEventTest> _myWeakEventTests = new List<MyWeakEventTest>();

	//弱引用管理
	private MyWeakEventManagerTest _myWeakEventManagerTest = new MyWeakEventManagerTest();

	#endregion

	public Window13()
	{
		InitializeComponent();
		Button_TestEdit_0.Click += Button_TestEdit_0_Click;
		Button_ClearView.Click += Button_ClearView_Click;
		Button_ClearModel.Click += Button_ClearModel_Click;
		Button_GC.Click += Button_GC_Click;
		Button_Rest.Click += Button_Rest_Click;
		reset();
	}

	#region 控件事件

	/// <summary>
	/// 重置视图
	/// </summary>
	private void Button_Rest_Click(object sender, RoutedEventArgs e)
	{
		reset();
	}

	/// <summary>
	/// GC
	/// </summary>
	private void Button_GC_Click(object sender, RoutedEventArgs e)
	{
		GC.Collect();
	}

	/// <summary>
	/// 清除模型即_myWeakEventTests
	/// </summary>
	private void Button_ClearModel_Click(object sender, RoutedEventArgs e)
	{
		_myWeakEventTests.Clear();
	}

	/// <summary>
	/// 清除视图即WrapPanel中的TextBox
	/// </summary>
	private void Button_ClearView_Click(object sender, RoutedEventArgs e)
	{
		WrapPanel_Default.Children.Clear();
	}

	/// <summary>
	/// 编辑_myWeakEventTests列表中第一个元素的WTest
	/// </summary>
	private void Button_TestEdit_0_Click(object sender, RoutedEventArgs e)
	{
		_myWeakEventTests[0].WTest = $"测试修改{_index}";
		_index++;
	}

	#endregion

	#region 辅助方法

	/// <summary>
	/// 重置视图即清除WrapPanel中的TextBox并重新添加
	/// </summary>
	private void reset()
	{
		_myWeakEventTests.Clear();
		WrapPanel_Default.Children.Clear();
		for (int i = 0; i < 30; i++)
		{
			var myWeakEventTest = new MyWeakEventTest() { WTest = i.ToString() };
			MyTextBox textBox = new MyTextBox() { Width = 100, Height = 30, Text = $"a{i}" };
			_myWeakEventManagerTest.AddLinstener(myWeakEventTest, new MyWeakEventBindingExpTest(textBox, MyTextBox.TextProperty, myWeakEventTest, "WTest"));
			_myWeakEventTests.Add(myWeakEventTest);
			WrapPanel_Default.Children.Add(textBox);
		}
	}

	#endregion
}

public class MyWeakEventTest : IMyWeakEventTest
{
	private string _wTest;

	public string WTest
	{
		get => _wTest;
		set
		{
			_wTest = value;
			PropertyChanged?.Invoke(this, nameof(WTest));
		}
	}

	public event Action<object, string> PropertyChanged;
}

        五个功能按钮的功能如字面意思,具体在代码注释中体现,代码创建了一个自定义的弱引用管理类(MyWeakEventManagerTest)以及一个通知模型列表(List<MyWeakEventTest>),窗口加载时向WrapPanel中增加了30个TextBox,并调用了MyWeakEventManagerTest的AddLinstener,这一步相当于SetBinding在AddLinstener时我们将MyWeakEventBindingExpTest设置到TextBox的附加属性WeakListenerProperty上并且更新目标,同时订阅IMyWeakEventTest.PropertyChanged。

        这时如果我们点击按钮"测试修改[0]",代码执行取_myWeakEventTests索引为0的值并设置WTest为[$"测试修改{_index}"],经过MyWeakEventManagerTest的中转最终执行了MyWeakEventBindingExpTest.ReceiveWeakEvent,目前MyWeakEventBindingExpTest中保存了Source、Target以及DProperty,同时ReceiveWeakEvent将我们修改的属性名给到了,那么顺理成章的将Target的DProperty修改为新的Source的值。

        说了这么多那么怎么证明它们之间没有耦合,下面通过内存变化来分析

        此时我们截取下内存快照,可以看到各个对象的计数, MyWeakEventTest(模型Source)、MyTextBox(控件Target)、MyWeakEventBindingExpTest(关系及通知管理)都是30个,和我们创建的相符,这个时候我们点击“清除View”即将MyTextBox移除,然后重新截取内存快照

        可以发现MyTextBox和 MyWeakEventBindingExpTest已经被回收,可以通过对象引用差异对比计数变化都为-30

         这时点击“重置”验证清除Model即将MyWeakEventTest移除,然后重新截取内存快照

        可以看到MyWeakEventTest已经被回收

6.总结

        之前看别人的博客,说代码语言不重要重要的是编程逻辑和思想,现在发现越来越是这个味儿了,之后有时间学习DependencyObject和DependencyProperty再分享

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: WPF MVVM(Model-View-ViewModel)是一种用于构建用户界面的软件架构模式。它的核心理念是将界面与业务逻辑分离,并通过数据绑定和命令绑定来实现数据与界面的同步更新。MVVM框架的源码是指用于实现MVVM模式的相关类和方法的代码。 WPF MVVM框架的源码通常包含以下几个部分: 1. Model:模型层,用于表示应用程序的数据和业务逻辑。它通常是一个POCO(Plain Old CLR Object)类,包含了应用程序需要处理的数据和相关的操作方法。 2. View:视图层,负责显示用户界面。它通常是XAML文件,用于定义界面的结构和外观。 3. ViewModel:视图模型层,作为View和Model之间的桥梁。它包含了与界面相关的数据、命令和业务逻辑,以及一些与界面交互的事件和方法。ViewModel通过继承INotifyPropertyChanged接口来通知View数据的变化,并通过命令绑定来处理用户操作。 MVVM框架的源码一般包含了上述三个部分的实现。它提供了一套类和方法,用于实现数据绑定、命令绑定、事件通知等核心功能。其中,数据绑定用于将View中的控件与ViewModel中的属性进行绑定,实现数据的双向同步更新;命令绑定用于将View中的控件事件与ViewModel中的命令进行绑定,实现用户交互的响应;事件通知用于通知View数据的变化,使界面能够及时更新。 WPF MVVM框架的源码通常由微软提供,并且可以从官方文档或开源社区获取。通过阅读和学习源码,开发人员可以更好地理解和掌握MVVM模式,以及如何使用框架提供的功能来构建高效、可扩展的WPF应用程序。同时,通过定制和扩展源码,开发人员还可以根据自己的需求进行个性化的开发。 ### 回答2: WPF(Windows Presentation Foundation)是一种用户界面框架,用于创建 Windows 应用程序。MVVM(Model-View-ViewModel)则是一种软件架构模式,用于将用户界面逻辑与业务逻辑分离。 WPF MVVM 框架的源码包含了实现 MVVM 架构模式所需的关键组件和类。这些源码的目的是提供一个基础架构,帮助开发人员更轻松地实现 MVVM 模式,提高开发效率和可维护性。 在 WPF MVVM 框架的源码中,主要包含以下几个部分: 1. 路由和绑定机制:WPF MVVM 框架提供了一套强大的路由和绑定机制,用于在视图和视图模型之间建立绑定关系。源码中包含了这些机制的实现细节,包括绑定表达式的解析、属性改变通知等。 2. 命令系统:MVVM 模式中,视图模型通过命令对象与视图进行交互。WPF MVVM 框架的源码提供了命令系统的实现,包括命令类的定义和注册、命令参数的传递等。 3. 数据绑定和转换:WPF MVVM 框架提供了丰富的数据绑定和转换机制,使视图与视图模型之间的数据传递更加灵活。源码中包含了数据绑定和转换的实现细节,包括绑定器、转换器等。 4. 依赖注入容器:MVVM 模式中,依赖注入是一种常见的设计模式,用于解耦视图模型与其他组件之间的依赖关系。WPF MVVM 框架的源码提供了依赖注入容器的实现,方便开发人员进行依赖管理。 总的来说,WPF MVVM 框架源码是一个完整的开发工具包,提供了实现 MVVM 模式所需的关键组件和类。通过阅读和理解源码,开发人员可以更好地理解 MVVM 模式的原理和应用,加快开发速度,并提高软件的可维护性和扩展性。 ### 回答3: WPF(Windows Presentation Foundation)是微软推出的一种用于创建适用于Windows应用程序的用户界面的技术,MVVM(Model-View-ViewModel)是一种设计模式,用于将应用程序的界面与其业务逻辑相分离。WPF MVVM框架源码包含了WPF技术与MVVM设计模式的结合实现,提供了一个开发桌面应用程序的基础。 WPF MVVM框架源码的核心是ViewModel,ViewModel是一个用于管理业务逻辑和数据的类。它与View(界面)和Model(数据)进行通信,并且通过数据绑定机制将数据从Model传递给View进行显示。ViewModel中包含了命令(Command)和属性(Property),通过命令可以响应用户的操作,属性则用于将数据从Model传递给View,也可以用于实现双向数据绑定。 WPF MVVM框架源码还包含了一些常用的辅助类和接口,用于简化开发过程。例如,INotifyPropertyChanged接口用于实现属性变化的通知,这样就可以在属性值变化时及时更新View。RelayCommand类用于实现命令的绑定和执行,可以直接在ViewModel中定义命令并与View进行绑定。另外,还有一些可以扩展WPF MVVM框架的第三方库,如Prism和MVVM Light等,它们提供了更多的功能和工具,可以进一步简化开发过程。 通过研究WPF MVVM框架源码,开发者可以深入了解WPFMVVM的底层实现原理,从而更好地掌握框架的使用和进行自定义扩展。同时,可以通过阅读源码学习一些最佳实践和设计思想,提高开发效率和代码质量。 总结来说,WPF MVVM框架源码是WPF技术和MVVM设计模式的结合实现,它提供了一个用于开发桌面应用程序的基础,包含了ViewModel、命令、属性等核心概念,以及一些辅助类和接口。通过研究源码,开发者可以深入了解框架的实现原理和最佳实践,提高开发效率和代码质量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值