Silverlight 4:如何使组合框在打开时刷新其ItemsSource

时间:2011-01-28 15:58:52

标签: c# silverlight-4.0 wcf-ria-services

我想在打开时刷新Silverlight 4组合框的内容。

这似乎是一件愚蠢的事情,但我不能为我的生活找到一个直接的答案。

我在Silverlight 4应用程序中有一个表单,其中有一个列出销售订单编号的组合框。很多人会同时打开这个表单,所以我希望它回拨给Web服务ON OPEN并刷新其内容。

我发现最接近的是这个:
http://blogs.msdn.com/b/kylemc/archive/2010/06/18/combobox-sample-for-ria-services.aspx

..我无法工作但无论如何也无法帮助我。该示例中没有任何内容允许我在打开时重新填充下拉列表。

我正在使用MVVM并使用Interactivity类将组合框的Open事件泵入我的ViewModel。在那里,我调用webservice并重置组合框'ItemsSource绑定的底层属性。不起作用 - 下拉列表闪烁一秒钟,然后打开,空白。

更新:

XAML:

<ComboBox x:Name="cmbOrderNumber" Width="125" 
                      ItemsSource="{Binding ActiveSalesOrderNumbers, Mode=TwoWay}" 
                      IsEnabled="{Binding OrderSelectorEnabled}">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="SelectionChanged">
                        <inf:InvokeDelegateCommandAction Command="{Binding SalesOrderSelectedCommand}" CommandParameter="{Binding ElementName=cmbOrderNumber, Path=SelectedValue}"></inf:InvokeDelegateCommandAction>
                    </i:EventTrigger>
                    <i:EventTrigger EventName="DropDownOpened">
                        <inf:InvokeDelegateCommandAction Command="{Binding SalesOrderOpenedCommand}"></inf:InvokeDelegateCommandAction>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </ComboBox>

C#:

 public void OnSalesOrderOpenedCommand(object o)
        {
            _companyContext.Load(_companyContext.GetSales_Order_Numbers_FromDateQuery(_lastSalesOrderRequest), q => 
            {
                if (!q.HasError)
                {
                    q.Entities.ToList().ForEach(e => 
                    {
                        ActiveSalesOrderNumbers.Add(e.Sales_Order_Number);
                    });
                    _lastSalesOrderRequest = DateTime.Now;
                }
                else
                {
                    throw new Exception("Error updating sales order number list.");
                }
            }, null);
        }

事件被触发,我看到数据按预期从服务返回,并将新项添加到ActiveSalesOrderNumbers,这是一个ObservableCollection。下拉列表不会更新,也不会有新记录。

2 个答案:

答案 0 :(得分:1)

您的商品清单是什么样的?如果您的ItemsSource被绑定到ViewModel上的ObservableCollection<T>,那么您应该能够添加/删除该集合中的项目,并且ComboBox项目将正确更新。 ComboBox因设置一次后真正搞砸了你的绑定而臭名昭着。我建议不要尝试替换整个ItemsSource,而是使用ObservableCollection<T>Clear()然后使用Add(...)

我正在做一个项目,因为我已经做了这件事,所以我可以验证它是否有效。

编辑:

MainPage.xaml中

<ComboBox
    x:Name="comboBox"
    ItemsSource="{Binding ActiveSalesOrderNumbers}"
    HorizontalAlignment="Center"
    Width="200"
    Height="27"
    Margin="30">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <StackPanel
                Orientation="Horizontal">
                <TextBlock>
                    <Run Text="{Binding SalesOrderNumber}"/>
                    <Run Text="{Binding LastModified}"/>
                </TextBlock>
            </StackPanel>
        </DataTemplate>
    </ComboBox.ItemTemplate>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="DropDownOpened">
            <ei:CallMethodAction MethodName="OnSalesOrderOpenedCommand" TargetObject="{Binding DataContext, ElementName=comboBox}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</ComboBox>

MainPage.xaml.cs中

public partial class MainPage : UserControl
{
    public MainPage()
    {
        InitializeComponent();

        this.DataContext = new MainPageViewModel();
    }
}

MainPageViewModel.cs

public class MainPageViewModel : NotifyObject
{
    private readonly SalesOrderRepository _repository;

    public MainPageViewModel()
        : this(new SalesOrderRepository())
    {
    }

    public MainPageViewModel(SalesOrderRepository repository)
    {
        _repository = repository;

        this.ActiveSalesOrderNumbers = new ObservableCollection<SalesOrder>();
    }

    private ObservableCollection<SalesOrder> _activeSalesOrderNumbers;
    public ObservableCollection<SalesOrder> ActiveSalesOrderNumbers
    {
        get { return _activeSalesOrderNumbers; }
        set
        {
            _activeSalesOrderNumbers = value;
            NotifyPropertyChanged(() => ActiveSalesOrderNumbers);
        }
    }

    public void OnSalesOrderOpenedCommand()
    {
        _repository.GetSalesOrderNumbers(result =>
        {
            this.ActiveSalesOrderNumbers.Clear();

            result.ToList().ForEach(e => { this.ActiveSalesOrderNumbers.Add(e); });
        });
    }
}

public class SalesOrder : NotifyObject
{
    private string _salesOrderNumber;
    public string SalesOrderNumber
    {
        get { return _salesOrderNumber; }
        set
        {
            _salesOrderNumber = value;
            NotifyPropertyChanged(() => SalesOrderNumber);
        }
    }

    private DateTime _lastModified;
    public DateTime LastModified
    {
        get { return _lastModified; }
        set
        {
            _lastModified = value;
            NotifyPropertyChanged(() => LastModified);
        }
    }
}

public class SalesOrderRepository
{
    public void GetSalesOrderNumbers(Action<IEnumerable<SalesOrder>> reply)
    {
        List<SalesOrder> orders = new List<SalesOrder>();

        for (int i = 0; i < 10; i++)
        {
            orders.Add(new SalesOrder { SalesOrderNumber = i.ToString(), LastModified = DateTime.Now });
        }

        reply(orders);
    }
}

NotifyObject.cs

public abstract class NotifyObject : INotifyPropertyChanged
{
    /// <summary>
    /// Occurs when a property value changes.
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged = delegate { };

    /// <summary>
    /// Raises this object's PropertyChanged event.
    /// </summary>
    /// <param name="propertyName">The property that has a new value.</param>
    protected void NotifyPropertyChanged(string propertyName)
    {
        this.VerifyPropertyName(propertyName);

        PropertyChangedEventHandler handler = this.PropertyChanged;
        if (handler != null)
        {
            var e = new PropertyChangedEventArgs(propertyName);

            handler(this, e);
        }
    }

    /// <summary>
    /// Raises this object's PropertyChanged event.
    /// </summary>
    /// <typeparam name="TProperty">The type of the property.</typeparam>
    /// <param name="property">The property.</param>
    protected void NotifyPropertyChanged<TProperty>(Expression<Func<TProperty>> property)
    {
        var lambda = (LambdaExpression)property;

        MemberExpression memberExpression;

        if (lambda.Body is UnaryExpression)
        {
            var unaryExpression = (UnaryExpression)lambda.Body;
            memberExpression = (MemberExpression)unaryExpression.Operand;
        }
        else memberExpression = (MemberExpression)lambda.Body;

        NotifyPropertyChanged(memberExpression.Member.Name);
    }

    /// <summary>
    /// Warns the developer if this object does not have
    /// a public property with the specified name. This 
    /// method does not exist in a Release build.
    /// </summary>
    [Conditional("DEBUG")]
    public void VerifyPropertyName(string propertyName)
    {
        // If you raise PropertyChanged and do not specify a property name,
        // all properties on the object are considered to be changed by the binding system.
        if (String.IsNullOrEmpty(propertyName))
            return;

        // Verify that the property name matches a real,  
        // public, instance property on this object.
        if (this.GetType().GetProperties().Where(p => p.Name == propertyName).FirstOrDefault() == null)
        {
            throw new ArgumentException(String.Format("Invalid property name: {0}", propertyName));
        }
    }
}

答案 1 :(得分:0)

为了能够在代码隐藏文件中绑定一个源,你需要使该文件成为DepencyProperty才能工作,例如:

    #region Title (DependenyProperty)
    public String Title
    {
        get { return (String)GetValue(TitleProperty); }
        set { SetValue(TitleProperty, value); }
    }
    public static readonly DependencyProperty TitleProperty =
        DependencyProperty.Register("Title", typeof(String), typeof(TopicListItem), new PropertyMetadata(null));
    #endregion String (DependenyProperty)

至少这是我理解它的作用。而不是string,而是创建您需要创建的集合对象。告诉我们它是怎么回事。