大佬教程收集整理的这篇文章主要介绍了wcf – Silverlight Combobox数据绑定竞争条件,大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
后端有两个表:一个是组件,一个是制造商.每个组件都有一个制造商.根本不是一个不寻常的外键查找关系.
我是Silverlight,我通过WCF服务访问数据.我将调用Components_Get(id)来获取Current组件(查看或编辑)以及@L_198_7@manufacturers_GetAll()以获取制造商的完整列表,以填充ComboBox的可能选择.然后,我将ComboBox上的SELEctedItem绑定到当前组件的制造商,将ComboBox上的Itemsource绑定到可能的制造商列表.像这样:
<UserControl.resources> <data:WebserviceDataManager x:Key="Webservice" /> </UserControl.resources> <Grid DataContext={Binding Components.Current,mode=OneWay,source={Staticresource WebservicE}}> <ComboBox Grid.Row="2" Grid.column="2" Style="{Staticresource ComboBoxStylE}" Margin="3" Itemssource="{Binding Manufacturers.All,Mode=OneWay,source={Staticresource WebservicE}}" SELEctedItem="{Binding Manufacturer,Mode=TwoWay}" > <ComboBox.ItemTemplate> <DataTemplate> <Grid> <TextBlock Text="{Binding NamE}" Style="{Staticresource DefaultTextStylE}"/> </Grid> </DataTemplate> </ComboBox.ItemTemplate> </ComboBox> </Grid>
这是最好的工作时间,直到我变得聪明并做了一点客户端缓存组件(我计划为制造商打开).当我打开组件的缓存并获得缓存命中时,所有数据都将正确存在于对象中,但SELEctedItem将无法绑定.原因是,在Silverlight中调用是异步的,并且在缓存的帮助下,在制造商之前不会返回Component.因此,当SELEctedItem尝试在Itemssource列表中找到Components.Current.Manufacturer时,它不存在,因为此列表仍然为空,因为尚未从WCF服务加载Manufacturers.All.同样,如果我关闭组件缓存,它会再次运行,但感觉不对 – 就像我很幸运时机正在运行.正确的修复恕我直言,MS用于修复ComboBox / ItemsControl控件,以了解这将发生Asynch调用是常态.但在那之前,我需要一种方法来解决它…
以下是我想到的一些选项:
>消除缓存或将其全面打开以再次掩盖问题.不好恕我直言,因为这将再次失败.不太愿意把它扫回地毯下.
>创建一个可以为我同步的中间对象(应该在ItemsControl本身中完成).当两者都到达时,它将接受和Item和ItemsList,然后输出和ItemWithItemsList属性.我会将ComboBox绑定到结果输出,以便它永远不会在另一个之前得到一个项目.我的问题是,这似乎是一种痛苦,但它会确保竞争条件不再发生.
任何想法/评论?
@Joe:非常感谢你的@L_489_35@.我知道只需要从UI线程更新UI.这是我的理解,我想我已经通过调试器确认了这一点,在SL2中,服务参考生成的代码为您解决了这个问题.即当我@L_198_7@manufacturers_Getall_Asynch()时,我通过Manufacturers_Getall_Completed事件获得结果.如果查看生成的服务引用代码,它可确保从UI线程调用* Completed事件处理程序.我的问题不是这个,而是我做了两个不同的调用(一个用于制造商列表,一个用于引用制造商id的组件),然后将这两个结果绑定到一个ComboBox.它们都绑定在UI线程上,问题是如果列表在选择之前没有到达那里,则忽略选择.
另请注意,这仍是if you just set the ItemSource and the SelectedItem in the wrong order的问题!
另一个更新:
虽然仍然存在组合框竞争条件,但我发现了其他一些有趣的东西.您永远不应该从该属性的“getter”中生成PropertyChanged事件.示例:在我的类型为ManufacturerData的SL数据对象中,我有一个名为“All”的属性.在Get {}中,它会检查它是否已被加载,如果没有,它会加载它:
public class ManufacturersData : DataserviceAccessbase { public ObservableCollection<Web.Manufacturer> All { get { if (!AllLoaded) LoadAllManufacturersAsync(); return mAll; } private set { mAll = value; OnPropertyChanged("All"); } } private void LoadAllManufacturersAsync() { if (!mCurrentlyLoadingAll) { mCurrentlyLoadingAll = true; // check to see if this component is loaded in local Isolated Storage,if not get it from the webservice ObservableCollection<Web.Manufacturer> all = IsoStorageManager.GetDataTransferObjectFromCache<ObservableCollection<Web.Manufacturer>>(mAllManufacturersIsoStoreFileName); if (null != all) { updateAll(all); mCurrentlyLoadingAll = false; } else { Web.SystemBuilderClient sbc = GetSystemBuilderClient(); sbc.Manufacturers_GetAllCompleted += new EventHandler<hookitupright.com.silverlight.data.Web.Manufacturers_GetAllCompletedEventArgs>(sbc_Manufacturers_GetAllCompleted); sbc.Manufacturers_GetAllAsync(); ; } } } private void updateAll(ObservableCollection<Web.Manufacturer> all) { All = all; AllLoaded = true; } private void sbc_Manufacturers_GetAllCompleted(object sender,hookitupright.com.silverlight.data.Web.Manufacturers_GetAllCompletedEventArgs E) { if (e.Error == null) { updateAll(e.Result.Records); IsoStorageManager.CacheDataTransferObject<ObservableCollection<Web.Manufacturer>>(e.Result.Records,mAllManufacturersIsoStoreFileName); } else OnWebserviceError(e.Error); mCurrentlyLoadingAll = false; } }
请注意,此代码在“缓存命中”时失败,因为它将在All {Get {}}方法中为“All”生成PropertyChanged事件,这通常会导致绑定系统再次调用All {get {}}. .我从ScottGu博客发布的方式复制了这种创建可绑定silverlight数据对象的模式,它总体上对我很有帮助,但是这样的东西使得它非常棘手.幸运的是,修复很简单.希望这有助于其他人.
在设置SELEctedItem之后设置Itemsource时存在问题.当发生这种情况时,Combobx将其视为选择的完全重置并清除SELEctedItem / SELEctedIndex.你可以在System.Windows.Controls.Primitives.SELEctor(ComboBox的基类)中看到这个:
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs E) { base.onItemsChanged(E); int SELEctedIndex = this.SELEctedIndex; bool flag = this.IsInit && this._initializingData.IsIndexSet; switch (e.Action) { case NotifyCollectionChangedAction.Add: if (!this.AddedWithSELEctionSet(e.NewStarTingIndex,e.NewStarTingIndex + e.NewItems.Count)) { if ((e.NewStarTingIndex <= SELEctedIndeX) && !flag) { this._processingSELEctionPropertyChange = true; this.SELEctedIndex += e.NewItems.Count; this._processingSELEctionPropertyChange = false; } if (e.NewStarTingIndex > this._focusedIndeX) { return; } this.SetFocusedItem(this._focusedIndex + e.NewItems.Count,falsE); } return; case NotifyCollectionChangedAction.Remove: if (((e.oldStarTingIndex > SELEctedIndeX) || (SELEctedIndex >= (e.oldStarTingIndex + e.oldItems.Count))) && (e.oldStarTingIndex < SELEctedIndeX)) { this._processingSELEctionPropertyChange = true; this.SELEctedIndex -= e.oldItems.Count; this._processingSELEctionPropertyChange = false; } if ((e.oldStarTingIndex <= this._focusedIndeX) && (this._focusedIndex < (e.oldStarTingIndex + e.oldItems.Count))) { this.SetFocusedItem(-1,falsE); return; } if (e.oldStarTingIndex < SELEctedIndeX) { this.SetFocusedItem(this._focusedIndex - e.oldItems.Count,falsE); } return; case NotifyCollectionChangedAction.replace: if (!this.AddedWithSELEctionSet(e.NewStarTingIndex,e.NewStarTingIndex + e.NewItems.Count)) { if ((e.oldStarTingIndex <= SELEctedIndeX) && (SELEctedIndex < (e.oldStarTingIndex + e.oldItems.Count))) { this.SELEctedIndex = -1; } if ((e.oldStarTingIndex > this._focusedIndeX) || (this._focusedIndex >= (e.oldStarTingIndex + e.oldItems.Count))) { return; } this.SetFocusedItem(-1,falsE); } return; case NotifyCollectionChangedAction.Reset: if (!this.AddedWithSELEctionSet(0,base.Items.Count) && !flag) { this.SELEctedIndex = -1; this.SetFocusedItem(-1,falsE); } return; } throw new InvalidoperationException(); }
注意最后一种情况 – 重置…当你加载一个新的Itemsource时,你最终会在这里被任何SELEctedItem / SELEctedIndex吹走?!?!
那么解决方案最终非常简单.我只是将错误的ComboBox子类化,并为此方法提供和覆盖如下.虽然我确实需要添加:
public class FixedComboBox : ComboBox { public FixedComboBox() : base() { // This is here to sync the dep properties (OnSELEctedItemChanged is private is the base class - thanks M$) base.SELEctionChanged += (s,E) => { FixedSELEctedItem = SELEctedItem; }; } // need to add a safe dependency property here to bind to - this will store off the "requested SELEctedItem" // this whole this is a kludgy wrapper because the OnSELEctedItemChanged is private in the base class public readonly static DependencyProperty FixedSELEctedItemProperty = DependencyProperty.Register("FixedSELEctedItem",typeof(object),typeof(FixedComboBox),new PropertyMetadata(null,new PropertyChangedCallBACk(FixedSELEctedItemPropertyChanged))); private static void FixedSELEctedItemPropertyChanged(DependencyObject obj,DependencyPropertyChangedEventArgs E) { FixedComboBox fcb = obj as FixedComboBox; fcb.mLastSELEction = e.NewValue; fcb.SELEctedItem = e.NewValue; } public object FixedSELEctedItem { get { return GetValue(FixedSELEctedItemProperty); } set { SETVALue(FixedSELEctedItemProperty,value);} } protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs E) { base.onItemsChanged(E); if (-1 == SELEctedIndeX) { // if after the base class is called,there is no SELEction,try if (null != mLastSELEction && Items.Contains(mLastSELEction)) SELEctedItem = mLastSELEction; } } protected object mLastSELEction = null; }
所有这一切都是(a)保存旧的SELEctedItem然后(b)检查是否在ItemsChanged之后,如果我们没有做出选择并且旧的SELEctedItem存在于新列表中……那么……选择它!
以上是大佬教程为你收集整理的wcf – Silverlight Combobox数据绑定竞争条件全部内容,希望文章能够帮你解决wcf – Silverlight Combobox数据绑定竞争条件所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。