c# - Race condition with converters? -
i have wpf form has quite few dynamically created controls e.g. combobox
es.
on each combobox
have few converters handle business logic. 1 of converters auto-populates n/a
, disabled based on other input on form. however, should allowed auto-populated (the user should not able select this).
to accomplish this, have converter on itemssource
of combobox filters out n/a
option if combobox
enabled, otherwise include can populated needed. have converter selecteditem auto-populate n/a
answer.
however, there seems race condition in converters not fire in same order consistently, resulting in blank answers (the converter auto-populate selecteditem running before itemssource converter running add answer).
is there anyway ensure converters execute in same manner? creating bindings/converters in code-behind (since controls created dynamically) if makes difference.
edit: adding in code per request (however question more of general question). here code behind defining bindings/converters (i have template of each control in xaml , cloning here well):
//filter out answeroptions (n/a, yes, no) based on other questions multibinding itemssourcebinding = new multibinding(); itemssourcebinding.bindings.add(new binding("parentform.baselinequestionsproperty.imageadequacyproperty.singleanswer") { source = }); itemssourcebinding.bindings.add(new binding(".") { source = containerbase }); itemssourcebinding.bindings.add(new binding("parentform.baselinequestionsproperty.missingtoptionproperty.isselected") { source = }); itemssourcebinding.bindings.add(new binding("parentform.baselinequestionsproperty.missingloptionproperty.isselected") { source = }); itemssourcebinding.bindings.add(new binding("answeroptions") { source = containerbase }); itemssourcebinding.converter = new endplateanswerfilterconverter(); bindingoperations.setbinding(combobox, combobox.itemssourceproperty, itemssourcebinding); //binding auto-populate answers based on image adequacy answer multibinding singleanswerbinding = new multibinding(); singleanswerbinding.bindings.add(new binding("parentform.baselinequestionsproperty.imageadequacyproperty.singleanswer") { source = }); singleanswerbinding.bindings.add(new binding(".") { source = containerbase }); singleanswerbinding.bindings.add(new binding("parentform.baselinequestionsproperty.missingtcoptionproperty.isselected") { source = }); singleanswerbinding.bindings.add(new binding("parentform.baselinequestionsproperty.missingloptionproperty.isselected") { source = }); singleanswerbinding.converter = new endplatenotreadabletoanswerconverter(); bindingoperations.setbinding(containerbase, containerbase.singleanswerproperty, singleanswerbinding);
here converters:
itemssource converter filter out answers:
public class endplateanswerfilterconverter : imultivalueconverter { public object convert(object[] values, system.type targettype, object parameter, system.globalization.cultureinfo culture) { if (values[0] answer && values[1] containerbase && values[2] bool && values[3] bool && values[4] observablecollection<answer> /*targettype == typeof(visibility)*/) { observablecollection<answer> itemssource = new observablecollection<answer>(); var answeroptions = (observablecollection<answer>)values[4]; var imageadequacyanswer = (answer)values[0]; var containerbase = (containerbase)values[1]; var missingtoptionselected = (bool)values[2]; var missingloptionselected = (bool)values[3]; //loop through answeroptions foreach (var ans in answeroptions) { //add n/a option if nr/missing images if (ans.value == ((int)yesnoanswers.notapplicable).tostring() && (imageadequacyanswer.value == ((int)imageadequacyanswers.notreadable).tostring() || (missingtoptionselected && containerbase.name.contains("_t")) || (missingloptionselected && containerbase.name.contains("_l")))) { itemssource.add(ans); } //add yes/no otherwise else if (ans.value != ((int)yesnoanswers.notapplicable).tostring() && !(imageadequacyanswer.value == ((int)imageadequacyanswers.notreadable).tostring() || (missingtoptionselected && containerbase.name.contains("_t")) || (missingloptionselected && containerbase.name.contains("_l")))) { itemssource.add(ans); } } return itemssource; } else { return null; } }
converter auto-populate combobox (containerbase custom user control encapsulates combobox
other properties well):
public class endplatenotreadabletoanswerconverter : imultivalueconverter { public object convert(object[] values, system.type targettype, object parameter, system.globalization.cultureinfo culture) { if (values[0] answer && values[1] containerbase && values[2] bool && values[3] bool /*targettype == typeof(visibility)*/) { var imageadequacyanswer = (answer)values[0]; var containerbase = (containerbase)values[1]; var missingtoptionselected = (bool)values[2]; var missingloptionselected = (bool)values[3]; if (imageadequacyanswer.value == ((int)imageadequacyanswers.notreadable).tostring() || (missingtoptionselected && containerbase.name.contains("_t")) || (missingloptionselected && containerbase.name.contains("_l"))) { return containerbase.getansweroptionbyvalue(((int)yesnoanswers.notapplicable).tostring()); } return null; } else { return null; } }
i thinking there might situation getting 2 different converters; hence 2 different data sets created , apparent race condition.
to solve possible issues such race condition recommend these 2 steps done; both of them done independently advised both together.
- within converter(s) put in
lock
sections , lock sync object stop dual access/creation of items go combos. - create singleton based converter converter responsible own creation , 1 converter regardless of page needs converter gets exact same one.
to have converter create recommend generic base class created handles singleton creation of converter , servers converter(s). basing following code off of blog article entitled xaml: call binding converter without defining staticresource in xaml markup derived base class in c#.
in article 1 never creates static resource converter in xaml, directly call converter in xaml such as:
<window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:converters="clr-namespace:omega.operation.converters" ... > <combo itemssource="{binding combodata, converter={ converters:endplateanswerfilterconverter } }">
to achieve direct binding of converter , creation of one converter in singleton following base class need implemented:
/// <summary> /// creates xaml markup can allow converters (which inheirit form class) called directly /// without specify static resource in xaml markup. /// </summary> public class coverterbase<t> : markupextension t : class, new() { private static t _converter = null; public coverterbase() { } /// <summary>create , return static implementation of derived converter usage in xaml.</summary> /// <returns>the static derived converter</returns> public override object providevalue(iserviceprovider serviceprovider) { return _converter ?? (_converter = (t) activator.createinstance(typeof (t), null)); } }
finally converter need specify base:
namespace omega.operation.converters { public class endplateanswerfilterconverter : coverterbase<endplateanswerfilterconverter>, system.windows.data.ivalueconverter { public object convert(object value, type targettype, object parameter, system.globalization.cultureinfo culture) { lock (syncobject) {} } public object convertback(object value, type targettype, object parameter, system.globalization.cultureinfo culture) { lock (syncobject) {} } } }
Comments
Post a Comment