WPF mvvm binding, get/set value: issue with preventing update value - c#

I'm working in a WPF mvvm environment.
I have some binded vars and data from cs file to xaml.
One is different from others: it is the index of the selected tab in my tabsCollection. When the user has more than one tab opened and has got mods to save, I show him a dialog. If he cliks "ok", he proceed with the change of the tab, if he clicks "cancel", the tab must remain the same.
this is my code:
private int p_SelectedDocumentIndex;
public int SelectedDocumentIndex{ get { return p_SelectedDocumentIndex; }
set {
if (tabsCollection.Count() > 1 && CanSave() == true)
{
if (dm.ShowMessage1(ServiceContainer.GetService<DevExpress.Mvvm.IDialogService>("confirmYesNo")))
{
p_SelectedDocumentIndex = value;
base.RaisePropertiesChanged("SelectedDocumentIndex");
}
//else {
// CODE FOR NOT CHANGE THE VALUE
//}
}
else {
p_SelectedDocumentIndex = value;
base.RaisePropertiesChanged("SelectedDocumentIndex");
}
}
}
So, the question is: how can I not apply the change in the "set" section? (like an undo, I think)
This is simpliest way to do it, but, if this approach is incorrect, how can I do?
Previous failed attempts:
1)
p_SelectedDocumentIndex = p_SelectedDocumentIndex
base.RaisePropertiesChanged("SelectedDocumentIndex");
2)
base.RaisePropertiesChanged("SelectedDocumentIndex");
3)
nothing in the else branch

Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() => SelectedDocumentIndex= p_SelectedDocumentIndex ), DispatcherPriority.Send);
This call arranges to revert the UI state to where it was before the operation started

I solved it. I've took the solution from here:
http://blog.alner.net/archive/2010/04/25/cancelling-selection-change-in-a-bound-wpf-combo-box.aspx
public int SelectedDocumentIndex{ get { return p_SelectedDocumentIndex; }
set {
// Store the current value so that we can
// change it back if needed.
var origValue = p_SelectedDocumentIndex;
// If the value hasn't changed, don't do anything.
if (value == p_SelectedDocumentIndex)
return;
// Note that we actually change the value for now.
// This is necessary because WPF seems to query the
// value after the change. The combo box
// likes to know that the value did change.
p_SelectedDocumentIndex = value;
if (tabsCollection.Count() > 1 && CanSave() == true)
{
if (!dm.ShowMessage1(ServiceContainer.GetService<DevExpress.Mvvm.IDialogService>("confirmYesNo")))
{
Debug.WriteLine("Selection Cancelled.");
// change the value back, but do so after the
// UI has finished it's current context operation.
Application.Current.Dispatcher.BeginInvoke(
new Action(() =>
{
Debug.WriteLine("Dispatcher BeginInvoke " + "Setting CurrentPersonCancellable.");
// Do this against the underlying value so
// that we don't invoke the cancellation question again.
p_SelectedDocumentIndex = origValue;
DocumentPanel p = tabsCollection.ElementAt(p_SelectedDocumentIndex);
p.IsActive = true;
base.RaisePropertiesChanged("SelectedDocumentIndex");
}),
System.Windows.Threading.DispatcherPriority.ContextIdle,
null
);
// Exit early.
return;
}
}
// Normal path. Selection applied.
// Raise PropertyChanged on the field.
Debug.WriteLine("Selection applied.");
base.RaisePropertiesChanged("SelectedDocumentIndex");
}
}

If your VM class is derived from DependencyObject then you can change your property to a DependecyProperty with a coerce callback which enables "undo" as follows:
public int SelectedDocumentIndex
{
get { return (int)GetValue(SelectedDocumentIndexProperty); }
set { SetValue(SelectedDocumentIndexProperty, value); }
}
public static readonly DependencyProperty SelectedDocumentIndexProperty =
DependencyProperty.Register("SelectedDocumentIndex", typeof(int), typeof(MyViewModel), new PropertyMetadata(0,
(d, e) =>
{
//Callback after value is changed
var vm = (MyViewModel)d;
var val = (int)e.NewValue;
}, (d, v) =>
{
//Coerce before value is changed
var vm = (MyViewModel)d;
var val = (int)v;
if (vm.tabsCollection.Count() > 1 && vm.CanSave() == true)
{
if (vm.dm.ShowMessage1(ServiceContainer.GetService<DevExpress.Mvvm.IDialogService>("confirmYesNo")))
{
//no coerce is needed
return v;
}
else
{
//should coerce to the previous value
return VM.SelectedDocumentIndex;
}
}
else
{
//no coerce is needed
return v;
}
}));

Related

Get the value from the database, using the property in the user control

I am designing a user control that has inside this user control (label, text box and check box list), I defined a property for this user control that when we enter a value inside it, a value from the database for We returned
Apparently, there is no problem with the code I wrote, but I don't actually receive an answer.
I wanted to get your help to solve this problem.
I will send you the code I wrote in the properties RegionID.
Thank you for your cooperation
private int _regionID = 0;
public int RegionID
{
get
{
return _regionID;
}
set
{
_regionID = value;
if (value < 1 && value> 4)
{
_regionID = 0;
}
else
{
Models.DataBaseContext dataBaseContext = null;
try
{
dataBaseContext =
new Models.DataBaseContext();
Models.Region region
= dataBaseContext.Regions
.Where(current => current.Region_Id == value)
.FirstOrDefault();
captionLabelControl.Text = region.Region_Name;
}
catch (System.Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
}
}
}
}

Initialize value in dictionary only once, then just update

I'm currently working to track packages when they move between bays, these changes happen every dozen or so seconds, so I have several threads accessing some dictionaries. The idea is that the first few times, I will not have the lastBay value (the program is just starting), but when A == B for the first time, I save the value of A (the bay that the pacakage has landed into, therefore the last bay at which it has been) and then just update said value every time A == B again.
private void in_bay()
{
String line_type = getDictionaryValues("global", "line_type").getStringValue();
bool result = false;
switch (line_type)
{
case "A_1":
int A = getVariableFromBucket("A_Act").getIntValue();
int B = getVariableFromBucket("A_Next").getIntValue();
result = A == B ? true : false;
if (result)
{
setDictionaryValues("global", "lastBay", new Variable("UNSIGNED8") { binaryValue = Utils.intToByteArray(A) });
}
break;
}
setVariableInBucket("IN_BAY", BitConverter.GetBytes(result));
log("IN_BAY flag in BUCKET: " + getVariableFromBucket("IN_BAY").getBoolValue(), DEBUG);
if (getDictionaryValues("global", "lastBay").binaryValue != null)
{
log("lastBay value in global: " + getDictionaryValues("global", "lastBay").getIntValue(), DEBUG);
}
else
{
log("undefined bay",DEBUG);
}
}
I have a getDictionaryValue() function that returns the variables (or an empty one if it's not in the dictionary):
public Variable getDictionaryValues(String DictionaryName, String VarName)
{
try
{
return functionDictionary[DictionaryName][VarName];
}
catch (Exception)
{
Variable emptyVariable = new Variable()
{
additionalInfo = null,
arrivalTime = 0,
binaryValue = null,
numBits = 0,
signed = false,
varType = null
};
return emptyVariable;
}
}
and a setDictionaryValue() function that actually sets the values to the dictionary selected:
public void setDictionaryValues(String DictionaryName, String VariableName, Variable VaValue)
{
try
{
lock (GlobalConstants.filtersLock)
{
if (!functionDictionary.ContainsKey(DictionaryName))
{
functionDictionary.Add(DictionaryName, new Dictionary<String, Variable>());
}
if (!functionDictionary[DictionaryName].ContainsKey(VariableName))
{
functionDictionary[DictionaryName].Add(VariableName, Value);
}
else
{
functionDictionary[DictionaryName][VariableName] = Value;
}
}
}
catch (Exception e)
{
log("An error has ocurred when setting values to functionDictionary: "+ e,DEBUG);
throw new Exception(e.ToString());
}
}
The problem is that the first time A == B It logs correctly the values being received, but when the values change again (the package starts moving again) the code no longer displays the values of lastBay, as if the dictionary global no longer has a value for lastBay. I attach an image with a reference as to the expected results and the results obtained:
What am I missing here?
From the comment thread, it looks like the problem is that in_bay is being called on different object instances, and functionDictionary is a non-static field, so you're dealing with different dictionary instances each time.
I just want to take the opportunity to point out how much simpler your code could be if you just used classes and variables rather than adding dictionaries and "Variable" objects as a layer of abstraction.
private void in_bay()
{
string? line_type = BayState.line_type;
bool result = false;
if(line_type == "A_1")
{
int A = Bucket.A_Act;
int B = Bucket.A_Next;
result = A == B;
if (result)
{
BayState.lastBay = A;
}
}
Bucket.IN_BAY = result;
log("IN_BAY flag in BUCKET: " + Bucket.IN_BAY, DEBUG);
if (BayState.lastBay != null)
{
log("lastBay value in global: " + BayState.lastBay.Value, DEBUG);
}
else
{
log("undefined bay", DEBUG);
}
}
I can pretty much guarantee whatever "business need" is driving the desire for dictionaries and such can be accomplished in another way that still allows you to write clean and less error-prone code.

How to run async calls as sync

I am new to C# & Xamarin. I feel there would be some quick solution to achieve this, but I already consumed a day to find one. Please give me a hint so I can proceed in the right direction.
The method is to Resize a Window after getting Height & Width. The issue is the GetWidth() and GetHeight() runs on async mode while the main thread sets the Window.SetFrame(...), hence I am not able to resize the window.
One solution I think is of cascading and setting the Window frame in the innermost block.
private void ResizeWindow()
{
_webView.EvaluateJavaScript("document.readyState",
(complete, error) =>
{
if (complete != null)
{
GetHeight();
GetWidth();
CGRect windowRect = Window.Frame;
windowRect.Size = new CGSize(_width, _height);
Window.SetFrame(windowRect,true);
}
});
}
private void GetWidth()
{
_webView.EvaluateJavaScript("document.body.scrollWidth",
(scrollWidth, wError) =>
{
if (scrollWidth != null && nfloat.TryParse(scrollWidth.ToString(), out nfloat value))
{
_width = value;
}
});
}
private void GetHeight()
{
_webView.EvaluateJavaScript("document.body.scrollHeight",
(scrollHeight, hError) =>
{
if (scrollHeight != null && nfloat.TryParse(scrollHeight.ToString(), out nfloat value))
{
_height = value;
}
});
}
Xamarin.iOS already has a TaskCompletionSource-based wrapper for WKWebView.EvaluateJavaScript.
So you can just await the call to EvaluateJavaScriptAsync for the NSValue-based result and then convert it to a C# value type.
Since I know that javascript being run returns a number, I can cast the NSObject return (that actually is an NSValue) to a NSNumber and obtain a C# long from it.
Example:
var result = await webview.EvaluateJavaScriptAsync("document.body.scrollWidth");
var width = (result as NSNumber).Int64Value;
result = await webview.EvaluateJavaScriptAsync("document.body.scrollHeight");
var height = (result as NSNumber).Int64Value;

MVC5 C# jQuery Unobtrusive Validation with ignore and Show/Hide Message

My users have requested that I change one of the fields from being required to optional, but still show/hide the warning message. Trying to do this with as little refactoring as I can I added an allowsubmission property on my data annotation on the server and in the jquery method on the client (see below).
Is it possible to set an ignore class on an element while still hiding/showing the message? It seems the method fires the first time and then stops firing after the ignore class is added, so the message stays on the screen.
Or is there a better way? Thank you.
$(document).ready(function () {
$("form").validate().settings.ignore = ".ignore, :hidden";
});
$.validator.unobtrusive.adapters.add('dependentrange', ['minvalueproperty', 'maxvalueproperty', 'allowsubmission'],
function (options) {
options.rules.dependentrange = options.params;
if (options.message) {
$.validator.messages.dependentrange = options.message;
}
}
);
$.validator.addMethod('dependentrange', function (value, element, params) {
var minValue = parseFloat($('input[name="' + params.minvalueproperty + '"]').val());
var maxValue = parseFloat($('input[name="' + params.maxvalueproperty + '"]').val());
var currentValue = parseFloat(value);
// if there is a value check it. If for some reason the min and max can't be found return true because
// i do not know the values to validate. Usually that is a coding mistake
if (isNaN(currentValue) || minValue > currentValue || currentValue > maxValue) {
var message = $(element).attr('data-val-dependentrange');
$.validator.messages.dependentrange = $.validator.format(message, minValue, maxValue);
if (params.allowsubmission) {
// once this property is added, the method does not fire
$(element).addClass("ignore");
}
return false;
}
$(element).removeClass('ignore');
return true;
}, '');
I ended up using validators API to show and hide my own warning message while always return true.
$.validator.unobtrusive.adapters.add('dependentrange', ['minvalueproperty', 'maxvalueproperty'], function (options) {
options.rules['dependentrange'] = options.params;
if (options.message) {
options.messages['dependentrange'] = options.message;
}
});
$.validator.addMethod("dependentrange", function (value, element, params) {
var valKeyed = parseFloat(value),
$elem = $(element),
$warning = $elem.closest('div').nextAll('.alert-warning:first')
msg = $elem.data('val-dependentrange),
isValid = this.optional(element) || valKeyed >= parseFloat(params.minvalueproperty) && valKeyed <= parseFloat(params.maxvalueproperty);
// there are no from or two found, so just return true with no warning
if (!params.minvalueproperty || !params.maxvalueproperty) {
return true;
}
if (isValid) {
$warning.text('')
$warning.addClass('hidden');
}
else {
$warning.text(msg)
$warning.removeClass('hidden');
}
return true;
});

Why is my app setting not being retained?

I'm trying to save a simple app setting ("LanguagePairId") this way:
if (rdbtnEnglishPersian.IsChecked == true) // because "IsChecked" is a nullable bool, the "== true" is necessary
{
langPairId = 1;
}
else if (rdbtnEnglishGerman.IsChecked == true)
{
langPairId = 2;
}
else if (rdbtnEnglishSpanish.IsChecked == true)
{
langPairId = 3;
}
else if (rdbtnGermanSpanish.IsChecked == true)
{
langPairId = 4;
}
else if (rdbtnGermanPersian.IsChecked == true)
{
langPairId = 5;
}
else if (rdbtnSpanishPersian.IsChecked == true)
{
langPairId = 6;
}
AppSettings.Default.LanguagePairId = langPairId;
LanguagePairId is being assigned the expected value (if rdbtnEnglishSpanish is checked, it is assigned 3, etc.)
But trying to read the app setting value on app startup:
int langPairId;
public MainWindow()
{
InitializeComponent();
RecheckTheLastSelectedRadBtn();
}
private void RecheckTheLastSelectedRadBtn()
{
langPairId = AppSettings.Default.LanguagePairId;
switch (langPairId)
{
case 1:
rdbtnEnglishPersian.IsChecked = true;
break;
. . .
...fails -- AppSettings.Default.LanguagePairId is seen as 0 on restaring the app. Why? What must I do to get the value to be saved and restored?
I don't see a call to AppSettings.Default.Save() anywhere.
Without that, your changes to the settings won't be saved.
Try adding it immediately after you set the property. E.g.:
AppSettings.Default.LanguagePairId = langPairId;
AppSettings.Default.Save();

Categories

Resources