我想使用MVVM模式构建我的业务应用程序。我选择MVVM-Light是因为它适合我的需求。在我见过的所有关于MVVM-Light的例子中,都没有人使用WCF RIA。经典的MIX10示例使用同一项目中的服务,而WCF在Web项目中创建服务。问题是:构建WCF Ria创建的整个DomainContex的接口看起来非常困难(对我来说肯定很难!)但是如果没有接口,我怎么能构建一个假的DomainContex来在Blend和测试中使用呢?我是不是遗漏了什么?谢谢。
发布于 2010-07-21 02:24:58
实际上,我使用的解决方案是可混合的,并且使混合过程更加简单。
下面是我如何使用这个框架来实现这一点的示例:
public class FolderViewModel : VMBase
{
private string _subject = string.Empty;
private string _folderName = string.Empty;
private string _service = string.Empty;
private string _dept = string.Empty;
private string _efolderid = string.Empty;
private string _timingName = string.Empty;
private WorkplanBase _planBase = null;
private IEnumerable<PSCustomList> _timingOptions = null;
private decimal _totalvalue = 0;
public FolderViewModel()
{
registerForMessages();
if (IsInDesignMode)
{
// Code runs in Blend --> create design time data.
EFOLDERID = "0123456790123456790123456791";
Subject = "9999-00 - This is a test nVision Subject";
Service = "AUDCOMP";
Department = "AUDIT";
FolderName = "NVSN003000";
List<PSCustomList> listItems = new List<PSCustomList>();
listItems.Add(new PSCustomList()
{
ID = "1234",
ParameterValue = "Busy Season"
});
listItems.Add(new PSCustomList()
{
ID = "1134",
ParameterValue = "Another Season"
});
_timingOptions = listItems.ToArray();
_totalvalue = 12000;
PlanBase = new WorkplanBase()
{
ClientFee = 15000,
Timing = "1234"
};
}
}
}然后,在绑定在ViewModelLocator类中的Acutal View模型的构造器中定义所有样本数据。当您在blend中时,VMBase负责不尝试实例化DataContext。
发布于 2010-07-20 06:23:02
我发现对我来说效果很好的是以下内容(我从MVVM light和RIA业务模板中提取了部分内容)。
我构建了一个新的ViewModelBase类,继承自MVVM Light的ViewModelBase类,在这个类中我实现了一个DomainContext、可能挂起的操作列表、当前操作、IsBusy属性、一个SaveCommand和一个protected方法来记录由ViewModels创建的、从这个类继承的任何操作。
下面是一个示例:
public class VMBase : ViewModelBase
{
protected DomainContext _context;
protected IList<OperationBase> operations = new List<OperationBase>();
protected OperationBase currentOperation;
public VMBase()
{
if (IsInDesignMode == false)
{
_context = new BudgetContext();
_context.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(_context_PropertyChanged);
}
SaveChangesCommand = new RelayCommand(
() =>
{
currentOperation = _context.SubmitChanges((so) =>
{
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
OnSaveComplete();
SaveChangesCommand.RaiseCanExecuteChanged();
CancelChangesCommand.RaiseCanExecuteChanged();
});
},
null);
logCurrentOperation();
},
() =>
{
return _context != null && _context.HasChanges;
});
CancelChangesCommand = new RelayCommand(
() =>
{
_context.RejectChanges();
SaveChangesCommand.RaiseCanExecuteChanged();
CancelChangesCommand.RaiseCanExecuteChanged();
},
() =>
{
return _context != null && _context.HasChanges;
});
}
/// <summary>
/// This is called after Save is Completed
/// </summary>
protected virtual void OnSaveComplete() { }
void _context_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "HasChanges")
{
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
SaveChangesCommand.RaiseCanExecuteChanged();
CancelChangesCommand.RaiseCanExecuteChanged();
});
}
}
/// <summary>
/// Bind to Busy Indicator to show when async server
/// call is being made to submit or load data via the
/// DomainContext/
/// </summary>
public bool IsWorking
{
get
{
if (currentOperation != null)
return !currentOperation.IsComplete;
else
{
return operations.Any(o => o.IsComplete == false);
}
}
}
/// <summary>
/// Call this command to save all changes
/// </summary>
public RelayCommand SaveChangesCommand { get; private set; }
/// <summary>
/// Revert all changes not submitted
/// </summary>
public RelayCommand CancelChangesCommand { get; private set; }
/// <summary>
/// Occurs after each operation is completed, which was registered with logCurrentOperation()
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void currentOperation_Completed(object sender, EventArgs e)
{
currentOperation = null;
operations.Remove((OperationBase)sender);
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
RaisePropertyChanged("IsWorking");
});
}
/// <summary>
/// Logs and notifies IsBusy of the Current Operation
/// </summary>
protected void logCurrentOperation()
{
currentOperation.Completed += new EventHandler(currentOperation_Completed);
operations.Add(currentOperation);
RaisePropertyChanged("IsWorking");
}
/// <summary>
/// Just make sure any async calls are done
/// </summary>
public override void Cleanup()
{
// Clean own resources if needed
foreach (OperationBase op in operations)
{
if (op.IsComplete == false)
{
if (op.CanCancel)
op.Cancel();
}
}
base.Cleanup();
}
}然后,在实际的视图模型中,您可以专注于视图模型的属性和命令-所有的域上下文都已经为您连接好了。只需使用_context属性-例如:
void loadFolderInfo()
{
if (_context != null)
{
EntityQuery<eFolder> query = _context.GetEFoldersByFolderQuery(_efolderid);
currentOperation =
_context.Load<eFolder>(query,
new Action<LoadOperation<eFolder>>((lo) =>
{
if (lo.HasError)
{
Messenger.Default.Send<DialogMessage>(
new DialogMessage(lo.Error.Message, (mbr) =>
{}));
}
DispatcherHelper.CheckBeginInvokeOnUI(
() =>
{
eFolder myFolder = lo.Entities.First();
Subject = myFolder.eSubject;
FolderName = myFolder.eFolderName;
});
}), null);
logCurrentOperation();
}
}属性可能如下所示:
public string EFOLDERID
{
get
{
return _efolderid;
}
set
{
if (_efolderid == value)
{
return;
}
var oldValue = _efolderid;
_efolderid = value;
// Update bindings and broadcast change using GalaSoft.MvvmLight.Messenging
RaisePropertyChanged("EFOLDERID", oldValue, value, true);
loadFolderInfo();
}
}关键部分是VMBase类处理DomainContext的所有连接和管理。要做到这一点,在你的ViewModel实现中,确保将任何_context.BaseOperationCall(..)的返回值赋给currrentOperation,然后立即调用logCurrentOperation。之后,它就不用插手了。然后,你可以将BusyIndicator绑定到你的IsWorking属性上,这样你就有了一个简化的RIA实现。
希望这篇文章能帮助你入门。
https://stackoverflow.com/questions/3236095
复制相似问题