Subject. This is happens at the moment when i'm closing application, and in section of CloseAsync() of my ViewModel trying to save attached model, that is inherited from SavableModelBase.
My ViewModel:
public ServerTabViewModel(Server server)
{
Argument.IsNotNull(() => server);
Server = server;
}
#region Properties
[Model]
public Server Server
{
get { return GetValue<Server>(ServerProperty); }
set { SetValue(ServerProperty, value); }
}
public static readonly PropertyData ServerProperty = RegisterProperty("Server", typeof(Server));
[ViewModelToModel("Server")]
public string ServerIpAddress
{
get { return GetValue<string>(ServerIpAddressProperty); }
set { SetValue(ServerIpAddressProperty, value); }
}
public static readonly PropertyData ServerIpAddressProperty = RegisterProperty("ServerIpAddress", typeof(string));
...
#endregion
protected override async Task CloseAsync()
{
var server = new Server
{
ServerIpAddress = ServerIpAddress, // ServerIpAddress is null now and model property (Server.ServerIpAddress) too.
...
};
SettingsService.SaveServer(server);
}
My Model:
public class Server : SavableModelBase<Server>
{
public string ServerIpAddress
{
get { return GetValue<string>(ServerIpAddressProperty); }
set { SetValue(ServerIpAddressProperty, value); }
}
public static readonly PropertyData ServerIpAddressProperty = RegisterProperty("ServerIpAddress", typeof(string));
...
}
In case, if i remove attribute [ViewModelToModel("Server")] on ServerIpAddress property of my ViewModel, value is available. It is predictable - no longer due on the property with a model.
How can I get the model does not set their properties to null at the moment when i'm closing my application? And why this happens?
I don't have a lot of code to go by, but I think this is caused by the views being unloaded. This means that your vm will be canceled (not saved since there is no explicit save) and your models will be reset to reset to the original state.
You can change this behavior by setting a property on the Model attribute:
[Model(SupportIEditableObject = false)]
public Server Server
{
...
}
Related
Our MVC web app is load balanced and makes use of the session to persist data out of proc in SQL server. Web config is set up like so:
<sessionState mode="SQLServer" allowCustomSqlDatabase="true" sqlConnectionString="Data Source..."/>
Using dependency injection we pass a class wrapping our session to the various controllers
public class MyWebSession : IWebSession
{
private const MyModelKey = "MyModelKey"
public MyModel MyModel
{
get { return (MyModel ) HttpContext.Current.Session[MyModelKey]; }
set { HttpContext.Current.Session[MyModelKey] = value; }
}
}
My question is if in our controllers we do things like this:
var id = MyWebSession.MyModel.Id;
var description = MyModel.Id.Description;
Would that incur multiple trips to the database? Or has that hit already occurred when the http request is made and I can treat the session data as in-memory variables?
Current refers to ContextBase, which refers to HostContext, which looks like this:
public static Object HostContext
{
[System.Security.SecurityCritical] // auto-generated
get
{
ExecutionContext.Reader ec = Thread.CurrentThread.GetExecutionContextReader();
Object hC = ec.IllogicalCallContext.HostContext;
if (hC == null)
hC = ec.LogicalCallContext.HostContext;
return hC;
}
IllogicalCallContext looks like this:
internal class IllogicalCallContext
{
private Hashtable m_Datastore;
private Object m_HostContext;
internal struct Reader
{
IllogicalCallContext m_ctx;
public Reader(IllogicalCallContext ctx) { m_ctx = ctx; }
public bool IsNull { get { return m_ctx == null; } }
[System.Security.SecurityCritical]
public Object GetData(String name) { return IsNull ? null : m_ctx.GetData(name); }
public Object HostContext { get { return IsNull ? null : m_ctx.HostContext; } }
}
private Hashtable Datastore
{
get
{
if (null == m_Datastore)
{
// The local store has not yet been created for this thread.
m_Datastore = new Hashtable();
}
return m_Datastore;
}
}
internal Object HostContext
{
get
{
return m_HostContext;
}
set
{
m_HostContext = value;
}
}
public Object HostContext { get { return IsNull ? null : m_ctx.HostContext; } }
}
..etc.
Eventually you wind up in CallContext.HostContext, which looks like this:
internal Object HostContext
{
get
{
return m_HostContext;
}
set
{
m_HostContext = value;
}
}
So whatever behavior occurs is going to depend on what someone puts in that HostContext property. This is where the Reference Source ends, so presumably someone or something is putting a dictionary or caching object in there, though I suppose it could be a database endpoint.
By the way, somewhere in this labyrinth should be a SessionState object that looks like this:
https://github.com/microsoft/referencesource/blob/master/System.Web/State/SessionState.cs
Please excuse my ignorance for I am new to C#.
I am currently working on an MVVM project in which a viewmodel has multiple instantiated public variables that are data-bound to elements in a view (WPF). When these variables are changed they automatically update in my view. Take for instance the code segment below from my view model...
private string _displaybind;
public string DisplayBind
{
get { return _displaybind; }
set
{
SetProperty(ref _displaybind, value);
if (_displaybind.Length > 5000)
{
DisplayBind = _displaybind.Substring(_displaybind.IndexOf('\n') + 1);
}
}
}
By using the command DisplayBind = "Hello"; within my viewmodel I can push out text to a textbox I have located in my XAML view. Unfortunately, I have reached a point where I can not simply edit the value of DisplayBind.
I need to start a state machine within my viewmodel which will access several states (classes) in separate C# files. However, I have no idea how to receive, and more importantly edit the values within my viewmodel from these separate classes.
I start my state machine in my viewmodel using this...
IPMProgram ipmprogram = new IPMProgram();
ipmprogram.StartTheIPMProgram();
This is my IPMProgram class
public class IPMProgram
{
public IPMProgramState currentState = null;
public IPMProgram()
{
currentState = new BootBannerState(this);
}
public void StartTheIPMProgram()
{
while (true)
{
currentState.GetNextState();
}
}
}
This is my IPMProgramState class
public abstract class IPMProgramState
{
private IPMProgram ipmprogram;
public IPMProgram Ipmprogram
{
get { return ipmprogram; }
set { ipmprogram = value; }
}
public abstract void GetNextState();
}
And this is my BootBannerState class (The state I want to edit DisplayBind from)
class BootBannerState : IPMProgramState
{
public BootBannerState(IPMProgramState state)
:this(state.Ipmprogram)
{
}
public BootBannerState(IPMProgram ipmprograminstance)
{
this.Ipmprogram = ipmprograminstance;
}
public override void GetNextState()
{
//DisplayBind = "Hello"!
return;
}
}
Someone suggested that I should look into Dependency Injection, but I don't quite understand how it would work for me. What should I do?
Thank you for all of your help,
Tesnich
We are using HttpSessionStateBase to store messages in a set up similar to this working example:
public class HttpSessionMessageDisplayFetch : IMessageDisplayFetch
{
protected HttpSessionStateBase _session;
private IList<ICoreMessage> messages
{
get
{
if (_session[EchoCoreConstants.MESSAGE_KEY] == null)
_session[EchoCoreConstants.MESSAGE_KEY] = new List<ICoreMessage>();
return _session[EchoCoreConstants.MESSAGE_KEY] as IList<ICoreMessage>;
}
}
public HttpSessionMessageDisplayFetch()
{
if (HttpContext.Current != null)
_session = new HttpSessionStateWrapper(HttpContext.Current.Session);
}
public void AddMessage(ICoreMessage message)
{
if (message != null)
messages.Add(message);
}
public IEnumerable<IResultPresentation> FlushMessagesAsPresentations(IResultFormatter formatter)
{
var mToReturn = messages.Select(m => m.GetPresentation(formatter)).ToList();
messages.Clear();
return mToReturn;
}
}
When we pass in a QualityExplicitlySetMessage (which inherits from ICoreMessage, see below) it is saved correctly to messages.
This is how the object looks after being inserted into the messages list, at the end of AddMessage(ICoreMessage message) above.
But when we come to access it after changing controllers the inherited member's properties are null, which causes a variety of null reference exceptions.
This is how the object now looks after we call FlushMessagesAsPresentations. I've commented out var mToReturn... as this tries to access one of these null ref properties.
I'd like to ask the following:
Why is the HttpSessionStateBase failing to capture these values taken
by the inherited type?
Is this an issue in saving to the HttpSession or in retrieving?
Is this anything to do with, as I suspect, inheritance?
Or is the fact I'm potentially calling a new controller that dependency injects the HttpSessionMessageDisplayFetch causing an issue?
I'm a first-time poster so please let me know if I'm making any kind of faux pas - Super keen to learn! Any input is very welcome.
Some potentially useful code snippets:
QualityExplicitlySetMessage
public class QualityExplicitlySetMessage : QualityChangeMessage
{
public QualityExplicitlySetMessage(IQPossession before, IQPossession after, IQEffect qEffect)
: base(before, after, qEffect)
{
IsSetToExactly = true;
}
}
QualityChangeMessage - Working example
public abstract class QualityChangeMessage : CoreMessage, IQualityChangeMessage
{
protected PossessionChange Change;
public PossessionChange GetPossessionChange()
{
return Change;
}
protected QualityChangeMessage(IQPossession before, IQPossession after, IQEffect qEffect)
{
Change = new PossessionChange(before, after, qEffect);
StoreQualityInfo(qEffect.AssociatedQuality);
}
public override IResultPresentation GetPresentation(IResultFormatter formatter)
{
return formatter.GetQualityResult(this);
}
#region IQualityChangeMessage implementation
public int LevelBefore
{
get { return Change.Before.Level; }
}
//... And so on with values dependent on the Change property.
}
CoreMessage - Working example
public abstract class CoreMessage : ICoreMessage
{
public string MessageType
{
get { return GetType().ToString(); }
}
public string ImageTooltip
{
get { return _imagetooltip; }
set { _imagetooltip = value; }
}
public string Image
{
get { return _image; }
set { _image = value; }
}
public int? RelevantQualityId { get; set; }
protected void StoreQualityInfo(Quality q)
{
PyramidNumberIncreaseLimit = q.PyramidNumberIncreaseLimit;
RelevantQualityId = q.Id;
RelevantQualityName = q.Name;
ImageTooltip = "<strong>" + q.Name + "</strong><br/>" + q.Description + "<br>" +
q.EnhancementsDescription;
Image = q.Image;
}
public virtual IResultPresentation GetPresentation(IResultFormatter formatter)
{
return formatter.GetResult(this);
}
}
UserController - Working example.
public partial class UserController : Controller
{
private readonly IMessageDisplayFetch _messageDisplayFetch;
public UserController(IMessageDisplayFetch messageDisplayFetch)
{
_messageDisplayFetch = messageDisplayFetch;
}
public virtual ActionResult MessagesForStoryletWindow()
{
var activeChar = _us.CurrentCharacter();
IEnumerable<IResultPresentation> messages;
messages = _messageDisplayFetch.FlushMessagesAsPresentations(_storyFormatter);
var vd = new MessagesViewData(messages)
{
Character = new CharacterViewData(activeChar),
};
return View(Views.Messages, vd);
}
}
I simply want to pass parameter to the control. But it threw error "Input string was not in a correct format." Why?* *
Xaml
<Views:SomeView SecurityId="abc"></Views:SomeView>
Model:
class Data
{
public string Case { get; set; }
public Data(int _input)
{
if (_input==1)
{
Case = "First";
}
else
{
Case = "Second";
}
}
}
ViewModel:
class DataViewModel
{
public string GetData
{
get
{
return D.Case;
}
set
{
D.Case = value;
}
}
public Data D;
public DataViewModel(string i)
{
D = new Data(Convert.ToInt16(i));
}
}
MainWindow
public partial class SomeView : UserControl
{
public string SecurityId
{
get
{
return (string)GetValue(SecurityIdProperty);
}
set { SetValue(SecurityIdProperty, value); }
}
public static readonly DependencyProperty
SecurityIdProperty =
DependencyProperty.Register("SecurityId",
typeof(string), typeof(SomeView),
new PropertyMetadata(""));
public SomeView()
{
DataContext = new DataViewModel(SecurityId);
InitializeComponent();
}
}
You never listened for changes.
You construct your DataViewModel with the value that SecurityId has at the time of the constructor call. Which is the default "". Then you change the value to "abc" through XAML. But that change is not transported anywhere. It happens and nobody cares. The construction of your DataViewModel is already done.
Do you want to listen to changes? I cannot tell. You will need to register a change handler for your dependency property.
In your PropertyMetaData you can pass a changed event handler as second parameter, for example a static method:
public static readonly DependencyProperty
SecurityIdProperty =
DependencyProperty.Register("SecurityId",
typeof(string), typeof(SomeView),
new PropertyMetadata("", new PropertyChangedCallback(MyValueChanged)));
You can then have a method to handle changes:
private static void MyValueChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
{
// react on changes here
}
It's not an attached property by the way. It's a normal dependency property.
This is because, you are trying to parse "abc" as integer, but you are not handling exceptions caused by ConvertTo.Int16() method.
Write DataViewModel constructor like,
public DataViewModel(string i)
{
int value = 0;
int.TryParse(i, out value); // TryParse handles the exception itself.
D = new Data(value);
}
Update the model from the view model
I have read some post about the MVVM but I not sure if understand the
way that the view model is updating the model
Currently I have two text boxes in the UI which is bound to the XAML view and call to the view model when the event was raised .
when should be the place in the view model when I updating the model?
This is the view model
class ViewModel:INotifyPropertyChanged
{
private String _url;
private String _TemplateType;
public string URL
{
get { return _url; }
set
{
if (value != _url)
{
_url= value;
OnPropertyChanged("URL");
}
}
}
public string TemplateType
{
get { return _TemplateType; }
set
{
if (value != _TemplateType)
{
_TemplateType= value;
OnPropertyChanged("URL");
}
}
}
The model
internal class DefineAddinModel
{
public string TemplateType { get; set; }
public String URL { get; set; }
}
The ViewModel usually acts as a wrapper around the Model and contains a reference to the Model which is can update either in response to commands or automatically in property setters.
UPDATE:
Here's an example of having the VM act as a wrapper around the Model. This may seem useless in your example but you will find in many cases the VM's getters/setters need to do some sort of transformation on the values rather than simply passing them through.
class ViewModel:INotifyPropertyChanged
{
private DefineAddinModel model;
public string URL
{
get { return model.URL; }
set
{
if (value != model.URL)
{
model.url = value;
OnPropertyChanged("URL");
}
}
}
public string TemplateType
{
get { return model.TemplateType; }
set
{
if (value != model.TemplateType)
{
model.TemplateType = value;
OnPropertyChanged("TemplateType");
}
}
}
The better way to update your Model Is by using an event, its safer, so choose weather using a button click or lost focus, or whatever you want
void button_click(object sender,eventsarg e)
{
MyObj.URL = App.Locator.MyVM.MyDefineAddinModel.URL;// App.Locator because MVVMLight is tagged
MyObj.TemplateType = App.Locator.MyVM.MyDefineAddinModel.TemplateType ;
}
but personnaly i Use the following steps :
1- In your ViewModel create a CurrentItem object of type DefineAddinModel and without OnPropertyChanged then bind it to the View(UI) DataContext of the RootElement on the View )
2- for the model I use the INotifyPropertyChanged for each propery
3- after binding the datacontext of your root element to the CurrentItem of your ViewModel then bind just URL and TemplateType properties to your Controls, so any thing changes on the textbox will update CurrentItem properties
you can also chose the type of the binding (On LostFocus, or OnPropertyChanged)
You need to bind your TextBoxes to the two properties URL and TemplateType.
Try to use Commands (in the ViewModel)instead of events (in The CodeBehind) since you are in MVVM.
For updating the model : use a button with it's Command property bound to OnSave just like this example:
private String _url;
private String _TemplateType;
private DefineAddinModel _defineAddin;
public DefineAddinModel DefineAddin
{
get {return _defineAddin;}
set
{
_defineAddin = value;
OnPropertyChanged("DefineAddin");
}
}
public string URL
{
get { return _url; }
set
{
if (value != _url)
{
_url= value;
OnPropertyChanged("URL");
}
}
}
public string TemplateType
{
get { return _TemplateType; }
set
{
if (value != _TemplateType)
{
_TemplateType= value;
OnPropertyChanged("URL");
}
}
}
public RelayCommand OnSaved
{
get;
set;
}
public ViewModel()
{
DefineAddin = new DefineAddinModel();
OnSaved = new RelayCommand(()=>
{
DefineAddin.URL = URL ;
DefineAddin.TemplateType = TemplateType;
});
Think about using third parties like MVVMLight it helps you a lot with MVVM and the helpers around it (Commands, Messenger, ViewModelLocator ...)
I think that the correct answer here is 'it depends'.
In most general cases, the advantage of actually using a ViewModel is also to track 'transient state', i.e. the state of an 'edit in progress' operation.
In this particular case, you would not push your changes directly to the Model every time a value is updated, instead you would do this via an 'Update' ICommand implementation that will collect all the data from the ViewModel and push it down to the Model.
This approach gives you many advantages:
The user of the view can change their mind as many times as they want, and only when they are happy will the Model actually get updated with their definitive choices
It greatly reduces the load on your persistence service, since only final changes are pushed through.
It allows you to do final validation on a complete set of values, rather than transient states, and hence reduces programming complexity and overhead.
It also makes your UI far more fluid since all the examples above are pushing updates on the UI Dispatcher, and avoids you having to cater for this via Tasks or other async approaches.
The backing model is never in an inconsistent state, since I would imagine that all values on one View/ViewModel are related, and only make sense when updated together using an ACID approach.
Here's an example of how I'd do it.
public class ViewModel:INotifyPropertyChanged {
private String _url;
private String _TemplateType;
public ViewModel(){
UpdateCommand = new DelegateCommand(OnExecuteUpdate, OnCanExecuteUpdate);
}
public bool OnCanExecuteUpdate(object param){
// insert logic here to return true when one can update
// or false when data is incomplete
}
public void OnExecuteUpdate(object param){
// insert logic here to update your model using data from the view model
}
public ICommand UpdateCommand { get; set;}
public string URL{
get { return _url; }
set {
if (value != _url) {
_url= value;
OnPropertyChanged("URL");
}
}
}
public string TemplateType {
get { return _TemplateType; }
set {
if (value != _TemplateType) {
_TemplateType= value;
OnPropertyChanged("TemplateType");
}
}
}
... etc.
}
public class DelegateCommand : ICommand {
Func<object, bool> canExecute;
Action<object> executeAction;
public DelegateCommand(Action<object> executeAction)
: this(executeAction, null) {}
public DelegateCommand(Action<object> executeAction, Func<object, bool> canExecute) {
if (executeAction == null) {
throw new ArgumentNullException("executeAction");
}
this.executeAction = executeAction;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter) {
bool result = true;
Func<object, bool> canExecuteHandler = this.canExecute;
if (canExecuteHandler != null) {
result = canExecuteHandler(parameter);
}
return result;
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged() {
EventHandler handler = this.CanExecuteChanged;
if (handler != null) {
handler(this, new EventArgs());
}
}
public void Execute(object parameter) {
this.executeAction(parameter);
}
}