2011-04-13 52 views
1

我有一個Silverlight模板控件(不是用戶控件),它包含一個ListBox。在Silverlight模板控件的ListBox DataTemplate中訪問按鈕點擊按鈕

在ListBox中我有一個按鈕的DataTemplate中,像這樣:

     <ListBox.ItemTemplate> 
          <DataTemplate> 
           <Grid> 
            <Grid.ColumnDefinitions> 
             <ColumnDefinition Width="Auto" /> 
             <ColumnDefinition Width="Auto" /> 
             <ColumnDefinition Width="*" /> 
            </Grid.ColumnDefinitions> 
            <ProgressBar Grid.Column="0" Width="70" Height="20" Value="{Binding Path=Percentage}" Minimum="0.0" Maximum="100.0" /> 
            <TextBlock Grid.Column="0" Text="{Binding Path=Percentage, StringFormat='{}{0:##0.0}%'}" Margin="10,3,3,3" HorizontalAlignment="Center" /> 
            <TextBlock Grid.Column="1" Text="{Binding Path=File.Name}" Margin="3" /> 
            <Button Grid.Column="2" Content="Remove" x:Name="RemoveButton" Command="{TemplateBinding DeleteCommand}" Style="{TemplateBinding UploadButtonStyle}" HorizontalAlignment="Right" Margin="0,0,5,0" /> 
           </Grid> 
          </DataTemplate> 
         </ListBox.ItemTemplate> 

看到按鈕出現在模板的結束?我如何訪問它的點擊事件?我不能使用GetTemplateChild()方法,因爲按鈕是DataTemplate的一部分。我試過了Commanding(正如你上面看到的)。似乎這是要走的路,儘管模板控制不完全是MVVM。

任何想法?也許是指揮之外的東西?否則我在指揮錯誤?

這裏的一些相關代碼:

...依賴屬性/屬性定義...(?它應該是一個德普道具)

public static readonly DependencyProperty DeleteCommandProperty = 
     DependencyProperty.Register("DeleteCommand", typeof(ICommand), typeof(MultipleFileUpload), new PropertyMetadata(null)); 

    public ICommand DeleteCommand 
    { 
     get { return (ICommand)GetValue(DeleteCommandProperty); } 
     set 
     { 
      SetValue(DeleteCommandProperty, value); 
      FirePropertyChanged("DeleteCommand"); //INotifyPropertyChanged stuff 
     } 
    } 

...在OnApplyTemplate().. 。

public override void OnApplyTemplate() 
    { 
     .... 
     DeleteCommand = new DelegateCommand(RemoveItemFromList, CanRemove); 
     .... 
     base.OnApplyTemplate(); 
    } 

...的ICommand的行動......

private void RemoveItemFromList(object commandParameter) 
    { 
     //NEVER GETTING HERE! 
    } 

我希望它是小事。

謝謝大家!

凱文

回答

1

我添加了一個命令,一個屬性I類綁定到列表框的(和其他的ItemsControl的)的ItemSource對象。這確實意味着我必須更改我的「數據」對象來處理GUI事件 - 這往往看起來是錯誤和黑客。

我也派生ItemsControl(但因爲列表框是一個ItemsControl這可能仍然適用)。我將自己的屬性添加到我最終想要從項目訪問的派生控件中。在你的情況下,按鈕命令處理程序。應該很容易設置這些屬性,因爲它們沒有被鎖定在該嵌套模板中。

接下來,我重寫了該派生類中的GetContainerForItemOverride(),並返回了另一個類,即我自己派生的ContentPresenter。這個新的ContentPresenter也應該具有相同的命令屬性 - 在構建它時將它設置爲等於在GetContainerForItemOverride中的ItemControl命令。

現在在DataTemplate中使用TemplateBinding(而不是常規綁定)來到該命令。

我周圍的努力使這一切的通用/可重複使用的版本的項目踢。

編輯,基本的例子:再次

class MyItemsControl : ItemsControl 
{ 
    public Command MyCommand {get;set;} // I've often use a full-blown DP here 

snip 

    protected override DependencyObject GetContainerForItemOverride() 
    { 
     return new MyContentPresenter(this.MyCommand); // MyContentPresenter is just a derived ContentPresenter with that same property. 
    } 

編輯:

我也把代碼ItemsControl.PrepareContainerForItemOverride。這種方法提供給你的ContentControl中(你自己一個人,如果你要覆蓋GetContainerForItemOverride),並在列表中的當前「項目」。在這裏,您還可以進一步初始化ContentControl實例 - 如果您想要執行的操作取決於它所綁定的對象。

+0

以下是XAML中如何將列表框放在一起的簡要說明:http://blogs.msdn.com/b/mikehillberg/archive/2006/09/28/briefanatomyofalistbox.aspx – Aardvark 2011-04-13 15:34:51

+0

謝謝對於這個建議。我正在嘗試第二種方法。快速的問題 - 在我的派生ContentPresenter(我以前從來沒有得到的其中之一..)我如何設置命令屬性等於從ItemControl的人?對不起,感謝您的幫助。 – kmk 2011-04-13 15:41:06

+0

它在派生的ItemsControl中創建派生ContentPresenter的新實例。您可以在ItemsControl.GetContainerForItemOverride()中執行此操作。我會嘗試編輯答案... – Aardvark 2011-04-13 15:57:41

1

我建議你使用一個relaycommand:

public class RelayCommand<T> : ICommand 
{ 
    #region Fields 

    readonly Action<T> _execute = null; 
    readonly Predicate<T> _canExecute = null; 

    #endregion // Fields 

    #region Constructors 

    public RelayCommand(Action<T> execute) 
     : this(execute, null) 
    { 
    } 

    /// <summary> 
    /// Creates a new command. 
    /// </summary> 
    /// <param name="execute">The execution logic.</param> 
    /// <param name="canExecute">The execution status logic.</param> 
    public RelayCommand(Action<T> execute, Predicate<T> canExecute) 
    { 
     if (execute == null) 
      throw new ArgumentNullException("execute"); 

     _execute = execute; 
     _canExecute = canExecute; 
    } 

    #endregion // Constructors 

    #region ICommand Members 

    [DebuggerStepThrough] 
    public bool CanExecute(object parameter) 
    { 
     return _canExecute == null ? true : _canExecute((T)parameter); 
    } 

    public event EventHandler CanExecuteChanged 
    { 
     add { CommandManager.RequerySuggested += value; } 
     remove { CommandManager.RequerySuggested -= value; } 
    } 

    public void Execute(object parameter) 
    { 
     _execute((T)parameter); 
    } 

    #endregion // ICommand Members 
} 

XAML:

<Button Grid.Column="2" Content="Remove" x:Name="RemoveButton" Command="{Binding DeleteCommand}" CommandParameter={Binding} Style="{TemplateBinding UploadButtonStyle}" HorizontalAlignment="Right" Margin="0,0,5,0" /> 

你點擊按鈕這是什麼會做的是每次,它會調用相同deletecommand,但會傳遞當前項目作爲參數。

希望這有助於

+0

感謝您的答覆。我嘗試過使用'Binding'和'TemplateBinding' - 我只是試着用你的建議來包含一個CommandParameter。仍未達到ICommand Action的斷點。 – kmk 2011-04-13 14:30:08

+0

Modfied我的回答,您還可以使用類似MVVMLight它有自己的RelayCommand的版本。 – 2011-04-13 14:36:11

+0

謝謝。我已經使用常規UserControls的命令運行過,但在模板控制的上下文中仍然沒有任何結果。我在Silverlight中,所以我沒有CommandManager。我想像MVVM Light有它自己的。也許我會嘗試。 – kmk 2011-04-13 14:47:19

1

我遇到this idea in MSDN,我還沒有嘗試過,但我想這是值得分享這裏:

在列表框中的項目的DataContext的是不一樣的觀點的DataContext。每個項目的DataContext引用集合中綁定到列表框的ItemsSource屬性的項目。

解決方案是將command屬性綁定到靜態資源,並將靜態資源的值設置爲要綁定的命令。這從股票交易者RI的以下XAML中進行了說明。

<!--Specifying the observablecommand in the view's resources--> 
<UserControl.Resources> 
    <Infrastructure:ObservableCommand x:Key="BuyCommand" /> 
</UserControl.Resources> 

<!—Binding the Button Click to the command. This control can sit inside a datagrid or a list box. --> 
<Button Commands:Click.Command="{Binding Path=Value, Source={StaticResource BuyCommand}}" Commands:Click.CommandParameter="{Binding Path=TickerSymbol}" /> 

然後在視圖中的代碼隱藏,你必須指定資源的價值實際上指向的演示模型的命令。以下是來自股票交易者RI的示例,其中呈現模型上的BuyCommand屬性放置在資源中。

((ObservableCommand)this.Resources["BuyCommand"]).Value = value != null ? value.BuyCommand : null; 
0

嗨,你可以使用相對來源和AncesterType。那麼它的作品適合我。

請參考下面的代碼。

<Button Content="Delete" Command="{Binding DataContext.DeleteCommand, 
      RelativeSource= {RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}" 
      CommandParameter="{Binding Path=SelectedItem, RelativeSource= {RelativeSource FindAncestor, AncestorType= 
       {x:Type ListBox}}}"/>