2011-01-28 4 views
2

Я хотел бы обновить содержимое Silverlight 4 combobox на открытии.Silverlight 4: Как сделать combobox обновить его ItemsSource при открытии

Это похоже на тупо-простую вещь, которую хочется делать, но я не могу для жизни меня найти прямой ответ.

У меня есть форма в приложении Silverlight 4 с выпадающим списком, в котором перечислены номера заказа клиента. У многих людей эта форма будет открыта в одно и то же время, поэтому я бы хотел, чтобы она перезвонила в веб-сервис ON OPEN и обновила его содержимое.

Ближе всего я нашел это:
http://blogs.msdn.com/b/kylemc/archive/2010/06/18/combobox-sample-for-ria-services.aspx

..which Я не мог работать и не в любом случае не поможет мне. В этом примере ничего нет, что позволяет мне повторно заполнять раскрывающийся список, когда он открывается.

Я использую MVVM и использовал классы интерактивности для перекачки события Open comboBox в мой ViewModel. Там я вызываю webservice и сбрасываю базовое свойство, которому привязан элемент ItemsSource comboboxes. Не работает - выпадающее меню мигает в течение секунды, а затем открывается, пустое.

UPDATE:

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. В раскрывающемся списке НЕ обновляется, нет новых записей.

ответ

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) 

Atleast это, как я понимаю, это работает , Вместо string вы создаете объект коллекции, который нужно создать. Расскажите, как это происходит.

+0

У меня есть событие, запускающее команду в моем ViewModel, нет проблем. Это работает. Команда вызывает веб-сервис и устанавливает найденную коллекцию в свойстве ViewModel. Проблема в том, что это асинхронный вызов, поэтому всплывающие окна всплывают до завершения вызова, и нет элементов. –

+0

Мои знания об асинхронном вызове немного шаткие. Не можете ли вы установить триггер, когда он откроется в первый раз? Однако это скорее хак, чем реальное решение исходной проблемы. Может быть, вам нужно загрузить данные перед загрузкой? – JeroenEijkhof

1

Как выглядит ваш список? Если ваш ItemsSource привязан к ObservableCollection<T> на вашем ViewModel, вы должны иметь возможность добавлять/удалять элементы из этой коллекции, а элементы ComboBox будут обновляться должным образом. ComboBox печально известен тем, что вы ввернули свои привязки, когда он был установлен один раз. Я бы предложил не пытаться заменить весь ItemSource, но использовать ObservableCollection<T>, Clear(), затем Add(...).

Я работаю над проектом прямо сейчас, когда я это сделал, поэтому я могу проверить, что он работает.

EDIT:

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)); 
     } 
    } 
} 
+0

@Joe: Итак, вы предлагаете добавлять элементы в коллекцию вместо замены? Я обязательно попробую это, спасибо! –

+0

@joe: Пробовал, не повезло. Обновлен кодом, который я использовал. –

+0

@ Dave - да, я сам не использовал событие DropDownOpened. Создание тестового проекта, который это делает. У вас была другая мысль, глядя на ваш код. Похоже, вы используете RIA Services. Вы можете изменить LoadBehavior на MergeIntoCurrent или RefreshCurrent. –

Смежные вопросы