请参阅下面的MCVE示例。
我的项目使用API来检索有关对象ExampleObject
的数据
在通过API进行其他查询之前,需要3个项ID
,Foo
和Bar
GetDataPoint()
:示例中为GetDataPoint()
。
我为每个项目提供了3个ComboBox,它们绑定到ObservableCollections。
因为我想自动返回结果,所以当任何ComboBox更改SelectedIndex时,我都会调用GetDataPoint()
。
过度调用
Refresh()
(Call: 1, ID: 4177, Foo: -1, Bar: -1 Call: 2, ID: 4177, Foo: -1, Bar: -1 Call: 3, ID: 4177, Foo: 32, Bar: 74 Call: 4, ID: 4177, Foo: 32, Bar: 74 Call: 5, ID: 4177, Foo: 32, Bar: -1 Call: 6, ID: 4177, Foo: 32, Bar: 74 Call: 7, ID: 4177, Foo: 32, Bar: 74
被调用时 8次),通常只有序列中的最后一次调用包含有效参数。
这不仅是性能问题,而且使用错误参数进行API调用会在后台抛出异常。
控制台输出示例:
GetDataPoint()
在选定的
Find_Foos()
,{所有参数有效后(Find_Bars()
和ID
完成了他们的事情之后),是否有任何方法可以恰好调用Foo
一次Bar
{1}}或<Window x:Class="CascadingDropDownLists.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:CascadingDropDownLists" mc:Ignorable="d" Title="MainWindow" Height="200" Width="600"> <Window.DataContext> <local:ViewModel/> </Window.DataContext> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition/> </Grid.RowDefinitions> <Button Margin="10" Content="Refresh" Width="100" Command="{Binding RefreshCommand}" /> <StackPanel Grid.Row="1" Margin="10" Orientation="Horizontal"> <Label Content="Example Object ID: " Margin="20,0,0,0"/> <ComboBox MinWidth="80" ItemsSource="{Binding IDCollection}" SelectedIndex="{Binding Selected_ID, Mode=TwoWay}"/> <Label Content="Foo: " Margin="20,0,0,0" /> <ComboBox MinWidth="80" ItemsSource="{Binding FooCollection}" SelectedIndex="{Binding Selected_Foo, Mode=TwoWay}"/> <Label Content="Bar: " Margin="20,0,0,0" /> <ComboBox MinWidth="80" ItemsSource="{Binding BarCollection}" SelectedIndex="{Binding Selected_Bar, Mode=TwoWay}"/> </StackPanel> <TextBlock Grid.Row="2" Margin="10" Text="{Binding Result}" /> </Grid> </Window>
更改?
namespace CascadingDropDownLists
{
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Collections.ObjectModel;
class ViewModel : INotifyPropertyChanged
{
private ObservableCollection<int> _idCollection;
public ObservableCollection<int> IDCollection
{
get { return _idCollection; }
set { _idCollection = value;
OnPropertyChanged("IDCollection");
}
}
private int _selected_ID;
public int Selected_ID
{
get { return _selected_ID; }
set { _selected_ID = value;
OnPropertyChanged("Selected_ID");
Selected_ID_Changed();
}
}
private ObservableCollection<int> _fooCollection;
public ObservableCollection<int> FooCollection
{
get { return _fooCollection; }
set { _fooCollection = value;
OnPropertyChanged("FooCollection");
}
}
private int _selected_Foo;
public int Selected_Foo
{
get { return _selected_Foo; }
set { _selected_Foo = value;
OnPropertyChanged("Selected_Foo");
Selected_Foo_Changed();
}
}
private ObservableCollection<int> _barCollection;
public ObservableCollection<int> BarCollection
{
get { return _barCollection; }
set { _barCollection = value;
OnPropertyChanged("BarCollection");
}
}
private int _selected_Bar;
public int Selected_Bar
{
get { return _selected_Bar; }
set { _selected_Bar = value;
OnPropertyChanged("Selected_Bar");
Selected_Bar_Changed();
}
}
private string result;
public string Result
{
get { return result; }
set { result = value;
OnPropertyChanged("Result");
}
}
private bool AlwaysExecute = true;
private DelegateCommand<bool> _refreshCommand;
public DelegateCommand<bool> RefreshCommand
{
get
{
if (_refreshCommand == null)
{
_refreshCommand = new DelegateCommand<bool>(
(s) => { Refresh(); },
(s) => { return AlwaysExecute; }
);
}
return _refreshCommand;
}
}
public ViewModel()
{
IDCollection = new ObservableCollection<int>();
FooCollection = new ObservableCollection<int>();
BarCollection = new ObservableCollection<int>();
}
public void Refresh()
{
DataFromModel.RefreshData();
Find_ExampleObjects();
}
private void Selected_ID_Changed()
{
Find_Foos();
Find_Bars();
GetDataPoint();
}
private void Selected_Foo_Changed()
{
Find_Bars();
GetDataPoint();
}
private void Selected_Bar_Changed()
{
GetDataPoint();
}
private void Find_ExampleObjects()
{
IDCollection.Clear();
FooCollection.Clear();
BarCollection.Clear();
foreach (int id in DataFromModel.GetExampleIDs())
{
IDCollection.Add(id);
}
if (IDCollection.Count > 0)
{
Selected_ID = 0;
}
}
private void Find_Foos()
{
FooCollection.Clear();
BarCollection.Clear();
if (IDCollection.Count > 0 && Selected_ID >= 0)
{
foreach (int foo in DataFromModel.GetFoos(IDCollection[Selected_ID]))
{
FooCollection.Add(foo);
}
}
if (FooCollection.Count > 0)
{
Selected_Foo = 0;
}
}
private void Find_Bars()
{
BarCollection.Clear();
if (IDCollection.Count > 0 && Selected_ID >= 0)
{
if (FooCollection.Count > 0 && Selected_Foo >= 0)
{
foreach (int ot in DataFromModel.GetBars(IDCollection[Selected_ID], FooCollection[Selected_Foo]))
{
this._barCollection.Add(ot);
}
}
}
if (BarCollection.Count > 0)
{
Selected_Bar = 0;
}
}
private DateTime lastCalled = DateTime.Now;
private int timesCalled = 0;
private void GetDataPoint()
{
int ID;
int Foo = -1;
int Bar = -1;
if (DateTime.Now.Ticks - lastCalled.Ticks >= 10000000)
{
lastCalled = DateTime.Now;
timesCalled = 1;
Console.WriteLine(Environment.NewLine + Environment.NewLine);
}
else { timesCalled++; }
if (IDCollection.Count > 0 && Selected_ID >= 0)
{
ID = IDCollection[Selected_ID];
if (this.FooCollection.Count > 0 && Selected_Foo >= 0)
{
Foo = this.FooCollection[Selected_Foo];
}
if (this.BarCollection.Count > 0 && Selected_Bar >= 0)
{
Bar = this.BarCollection[Selected_Bar];
}
Result = DataFromModel.GetSomeInterestingData(ID, Foo, Bar) + Environment.NewLine
+ "Times GetDataPoint() Called in the last second: " + timesCalled.ToString();
Console.WriteLine("Call: {0}, ID: {1}, Foo: {2}, Bar: {3}", timesCalled, ID, Foo, Bar);
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
/// <summary>
/// Each Object must have an ID.
/// Each Object will have at least 1 Foo, up to max 10.
/// Each Foo must have a Foo Number (1 to 100)
/// Each Foo might or might not contain any bars (1 to 100)
/// </summary>
public class ExampleObject
{
public int ID { get; set; }
public List<Foo> Foos { get; private set; }
public ExampleObject(Random rnd, int id)
{
// Generate example data
int howManyFoos = rnd.Next(1, 10);
Foos = new List<Foo>();
for (int f = 0; f < howManyFoos; f++)
{
int newFooNumber = rnd.Next(1, 100);
Foo foo = new Foo(newFooNumber);
int howManyBars = rnd.Next(0, 12);
for (int b = 0; b < howManyBars; b++)
{
int newBar = rnd.Next(1, 100);
foo.Bars.Add(newBar);
}
Foos.Add(foo);
}
ID = id;
}
}
public class Foo
{
public int FooNumber { get; private set; }
public List<int> Bars { get; set; }
public Foo(int fooNumber)
{
FooNumber = fooNumber;
Bars = new List<int>();
}
}
// Mock-up of a data source API
public static class DataFromModel
{
private static Dictionary<int, ExampleObject> HiddenStuff;
static DataFromModel()
{
HiddenStuff = new Dictionary<int, ExampleObject>();
}
public static void RefreshData()
{
HiddenStuff.Clear();
Random rnd = new Random();
// Create up to 10 example objects
for (int i = 0; i < 10; i++)
{
int newID = rnd.Next(1, 5000);
if (!HiddenStuff.ContainsKey(newID))
{
HiddenStuff.Add(newID, new ExampleObject(rnd, newID));
}
}
}
public static List<int> GetExampleIDs()
{
List<int> l = new List<int>();
foreach (KeyValuePair<int, ExampleObject> kvp in HiddenStuff)
{
l.Add(kvp.Key);
}
return l;
}
public static List<int> GetFoos(int id)
{
List<int> foos = new List<int>();
if (HiddenStuff.ContainsKey(id))
{
foreach (Foo f in HiddenStuff[id].Foos)
{
foos.Add(f.FooNumber);
}
}
return foos;
}
public static List<int> GetBars(int id, int fooNumber)
{
List<int> bars = new List<int>();
if (HiddenStuff.ContainsKey(id))
{
foreach (Foo f in HiddenStuff[id].Foos)
{
if (f.FooNumber == fooNumber)
{
foreach (int bar in f.Bars)
{
bars.Add(bar);
}
}
}
}
return bars;
}
public static string GetSomeInterestingData(int id, int foo, int bar = -1)
{
if (bar == -1)
{
return "Something Returned: " + id.ToString() + ", " + foo.ToString();
}
else
{
return "Something Returned: " + id.ToString() + ", " + foo.ToString() + ", " + bar.ToString();
}
}
}
}
ViewModel.cs
using System;
namespace CascadingDropDownLists
{
public class DelegateCommand<T> : System.Windows.Input.ICommand
{
private readonly Predicate<T> _canExecute;
private readonly Action<T> _execute;
public DelegateCommand(Action<T> execute)
: this(execute, null)
{
}
public DelegateCommand(Action<T> execute, Predicate<T> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (_canExecute == null)
return true;
return _canExecute((parameter == null) ? default(T) : (T)Convert.ChangeType(parameter, typeof(T)));
}
public void Execute(object parameter)
{
_execute((parameter == null) ? default(T) : (T)Convert.ChangeType(parameter, typeof(T)));
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, EventArgs.Empty);
}
}
}
DelegateCommand.cs
{{1}}
答案 0 :(得分:0)
我设法用一种锁来解决这个问题。
现在GetDataPoint()
只被调用一次,它总是在找到foos和bar之后发生,所以我有有效的参数传递。
private bool ignoreSelectionChanges = false;
private void Selected_ID_Changed()
{
ignoreSelectionChanges = true;
Find_Foos();
Find_Bars();
ignoreSelectionChanges = false;
GetDataPoint();
}
private void Selected_Foo_Changed()
{
if (!ignoreSelectionChanges)
{
ignoreSelectionChanges = true;
Find_Bars();
ignoreSelectionChanges = false;
GetDataPoint();
}
}
private void Selected_Bar_Changed()
{
if (!ignoreSelectionChanges)
{
GetDataPoint();
}
}