2013-02-26 76 views
1

我有一個WPF Grid並希望根據用戶的輸入向上或向下移動行。這是我迄今爲止所嘗試過的(用戶決定移動元素時的一個示例):如何在網格中移動項目

RowDefinition currentRow = fieldsGrid.RowDefinitions[currentIndex]; 
fieldsGrid.RowDefinitions.Remove(currentRow); 
fieldsGrid.RowDefinitions.Insert(currentIndex - 1, currentRow); 

我做錯了什麼?由於使用這種方法的UI保持不變。

+1

'我有一個WPF網格,並希望根據用戶的輸入向上或向下移動行 - 你想要什麼?請澄清你的意圖,因爲可能有更好更簡單的方法來做你所需要的。 – 2013-02-26 16:40:45

+0

這聽起來很奇怪和錯誤。如果你有一組需要根據用戶輸入移動的數據,那麼你應該使用容易實現的控件,即Listbox,Datagrid等... – 2013-02-26 16:44:15

+0

我編輯了這個問題來添加一些上下文@BrentStewart – 2013-02-26 16:49:59

回答

3

這將是WPF的方法來你的截圖是什麼樣子:

<Window x:Class="WpfApplication4.Window9" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="Window9" Height="300" Width="500"> 
    <ItemsControl ItemsSource="{Binding Columns}"> 
     <ItemsControl.ItemTemplate> 
      <DataTemplate> 
       <DataTemplate.Resources> 
        <BooleanToVisibilityConverter x:Key="BoolToVisConverter"/> 
       </DataTemplate.Resources> 
       <Grid> 
        <Grid.ColumnDefinitions> 
         <ColumnDefinition Width="20"/> 
         <ColumnDefinition Width="50"/> 
         <ColumnDefinition/> 
         <ColumnDefinition Width="100"/> 
         <ColumnDefinition Width="25"/> 
         <ColumnDefinition Width="25"/> 
        </Grid.ColumnDefinitions> 

        <!-- This is your Key image, I used a rectangle instead, you can change it --> 
        <Rectangle Fill="Yellow" Visibility="{Binding IsPrimaryKey, Converter={StaticResource BoolToVisConverter}}" Margin="2"/> 

        <CheckBox IsChecked="{Binding IsSelected}" Grid.Column="1"/> 

        <TextBlock Text="{Binding Name}" Grid.Column="2"/> 

        <ComboBox ItemsSource="{Binding SortOrders}" SelectedItem="{Binding SortOrder}" Grid.Column="3" Margin="2"/> 

        <Button Content="Up" Grid.Column="4" Margin="2" 
          Command="{Binding DataContext.MoveUpCommand, RelativeSource={RelativeSource FindAncestor, AncestorType=ItemsControl}}" 
          CommandParameter="{Binding}"/> 

        <Button Content="Down" Grid.Column="5" Margin="2" 
          Command="{Binding DataContext.MoveDownCommand, RelativeSource={RelativeSource FindAncestor, AncestorType=ItemsControl}}" 
          CommandParameter="{Binding}"/> 

       </Grid> 
      </DataTemplate> 
     </ItemsControl.ItemTemplate> 
    </ItemsControl> 
</Window> 

代碼背後:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Windows; 
using InduraClientCommon.MVVM; 
using System.Collections.ObjectModel; 

namespace WpfApplication4 
{ 
    public partial class Window9 : Window 
    { 
     public Window9() 
     { 
      InitializeComponent(); 

      var vm = new ColumnListViewModel(); 
      vm.Columns.Add(new ColumnViewModel() { IsPrimaryKey = true, Name = "Customer ID", SortOrder = SortOrder.Ascending }); 
      vm.Columns.Add(new ColumnViewModel() {Name = "Customer Name", SortOrder = SortOrder.Descending}); 
      vm.Columns.Add(new ColumnViewModel() {Name = "Customer Age", SortOrder = SortOrder.Unsorted}); 

      DataContext = vm; 
     } 
    } 
} 

視圖模型:

public class ColumnListViewModel: ViewModelBase 
    { 
     private ObservableCollection<ColumnViewModel> _columns; 
     public ObservableCollection<ColumnViewModel> Columns 
     { 
      get { return _columns ?? (_columns = new ObservableCollection<ColumnViewModel>()); } 
     } 

     private DelegateCommand<ColumnViewModel> _moveUpCommand; 
     public DelegateCommand<ColumnViewModel> MoveUpCommand 
     { 
      get { return _moveUpCommand ?? (_moveUpCommand = new DelegateCommand<ColumnViewModel>(MoveUp, x => Columns.IndexOf(x) > 0)); } 
     } 

     private DelegateCommand<ColumnViewModel> _moveDownCommand; 
     public DelegateCommand<ColumnViewModel> MoveDownCommand 
     { 
      get { return _moveDownCommand ?? (_moveDownCommand = new DelegateCommand<ColumnViewModel>(MoveDown, x => Columns.IndexOf(x) < Columns.Count)); } 
     } 

     private void MoveUp(ColumnViewModel item) 
     { 
      var index = Columns.IndexOf(item); 
      Columns.Move(index, index - 1); 
      MoveUpCommand.RaiseCanExecuteChanged(); 
      MoveDownCommand.RaiseCanExecuteChanged(); 
     } 

     private void MoveDown(ColumnViewModel item) 
     { 
      var index = Columns.IndexOf(item); 
      Columns.Move(index, index + 1); 
      MoveUpCommand.RaiseCanExecuteChanged(); 
      MoveDownCommand.RaiseCanExecuteChanged(); 
     } 
    } 

    public class ColumnViewModel: ViewModelBase 
    { 
     private bool _isPrimaryKey; 
     public bool IsPrimaryKey 
     { 
      get { return _isPrimaryKey; } 
      set 
      { 
       _isPrimaryKey = value; 
       NotifyPropertyChange(() => IsPrimaryKey); 
      } 
     } 

     private bool _isSelected; 
     public bool IsSelected 
     { 
      get { return _isSelected; } 
      set 
      { 
       _isSelected = value; 
       NotifyPropertyChange(() => IsSelected); 
      } 
     } 

     private string _name; 
     public string Name 
     { 
      get { return _name; } 
      set 
      { 
       _name = value; 
       NotifyPropertyChange(() => Name); 
      } 
     } 

     private List<SortOrder> _sortOrders; 
     public List<SortOrder> SortOrders 
     { 
      get { return _sortOrders ?? (_sortOrders = Enum.GetValues(typeof(SortOrder)).OfType<SortOrder>().ToList()); } 
     } 

     private SortOrder _sortOrder; 
     public SortOrder SortOrder 
     { 
      get { return _sortOrder; } 
      set 
      { 
       _sortOrder = value; 
       NotifyPropertyChange(() => SortOrder); 
      } 
     } 
    } 

    public enum SortOrder {Unsorted, Ascending, Descending} 
} 

這是個什麼樣子像在我的屏幕上:

enter image description here

正如你在上面的例子中看到的,我沒有在代碼中操縱或創建UI元素,因爲它實際上並不是必需的。無論何時您需要與屏幕上顯示的信息進行交互,您都可以與ViewModel而不是View進行交互。這是UI和應用程序邏輯之間關係的明確分離WPF使其成爲可能,這在其他框架中完全沒有。在WPF中做任何類型的N元素UI時,請考慮這種方法是事實上的默認方式。

編輯:這種方法相對於classic之一

優點:

  • 無需操縱,以顯示你的代碼複雜WPF類(IE UI元素) /從屏幕獲取數據(只需簡單,簡單 屬性和INotifyPropertyChanged)
  • 更好的縮放(UI可以b只要它支持ViewModel屬性,就可以將ComboBox更改爲每個腳都有排序順序的旋轉3d 粉紅色大象。
  • 無需導航視覺樹來查找位於天知道位置的元素。
  • 不需要foreach什麼。只需一個簡單的Select即可將您的數據(來自您獲得的任何數據源)轉換爲 ViewModel列表。

底線:WPF是比什麼都更簡單,更好的其他當前存在,如果你使用WPF的辦法。

+0

@DotNet在ViewModelBase類中使用[基於表達式的NotifyPropertyChange](http://stackoverflow.com/questions/2711435/typesafe-notifypropertychanged-using-linq-expressions)。 – 2013-02-26 19:22:09

+0

@DotNET作爲一個旁白的評論,最好是以正確的方式來做事情,因爲最終能幫助你完成死路線和事情。花點時間分析一下,如何重新讀取您在代碼中創建的UI元素的所有數據,例如,一旦用戶按下「保存」或其他內容。 – 2013-02-26 19:30:16

+0

我完全同意你的看法HighCore :)唯一的問題是,如果我必須改變這部分應用程序,那麼大部分非常繁重的代碼將不得不離開。我必須權衡利弊,我想 – 2013-02-26 19:45:31

1

您正在更改RowDefinition的訂單,這不是您想要的。你想改變的元素的行分配,這是由Grid.Row attached property

確定我願意把屬於每一行的容器(每行一個)的所有控件,然後使用Grid.SetRow改變周圍的容器。請參閱how to change the grid row of the control from code behind in wpf

+0

這個問題是我想要行完美對齊 – 2013-02-26 16:50:45

+0

然後你應該迭代網格中的控件,並調用Grid.SetRow,或者使用其他的東西(比如像Brent Stewart的評論所建議的ListBox或DataGrid) ) – sinelaw 2013-02-26 16:57:18

+0

增加了一些代碼 - 你能推薦使用控制嗎? – 2013-02-26 17:32:07

3

下面是一個使用一個ItemsControl做你想要什麼樣的一個簡單的例子:

視圖模型

public class ListBoxViewModel 
{ 
    private static readonly List<string> sortList = new List<string>() { "Unsorted", "Sorted" }; 
    public List<string> SortList { get { return sortList; } } 

    public ObservableCollection<ItemDetail> ItemDetails { get; set; } 

    #region Up Command 
    ICommand upCommand; 
    public ICommand UpCommand 
    { 
     get 
     { 
      if (upCommand == null) 
      { 
       upCommand = new RelayCommand(UpExecute); 
      } 
      return upCommand; 
     } 
    } 

    private void UpExecute(object param) 
    { 
     var id = param as ItemDetail; 

     if (id != null) 
     { 
      var curIndex = ItemDetails.IndexOf(id); 
      if (curIndex > 0) 
       ItemDetails.Move(curIndex, curIndex - 1); 
     } 
    } 
    #endregion Up Command 

    #region Down Command 
    ICommand downCommand; 
    public ICommand DownCommand 
    { 
     get 
     { 
      if (downCommand == null) 
      { 
       downCommand = new RelayCommand(DownExecute); 
      } 
      return downCommand; 
     } 
    } 

    private void DownExecute(object param) 
    { 
     var id = param as ItemDetail; 
     if (id != null) 
     { 
      var curIndex = ItemDetails.IndexOf(id); 
      if (curIndex < ItemDetails.Count-1) 
       ItemDetails.Move(curIndex, curIndex + 1); 
     } 
    } 
    #endregion Down Command 

    public ListBoxViewModel() 
    { 
     ItemDetails = new ObservableCollection<ItemDetail>() 
     { 
      new ItemDetail() { IsSelected = false, ItemName = "Customer Id", SortOrder = "Unsorted" }, 
      new ItemDetail() { IsSelected = true, ItemName = "Customer Name", SortOrder = "Sorted" }, 
      new ItemDetail() { IsSelected = false, ItemName = "Customer Age", SortOrder = "Unsorted" } 
     }; 
    } 
} 

ItemDetail類(我做的最多使事情變得更容易)

public class ItemDetail 
{ 
    public bool IsSelected { get; set; } 
    public string ItemName { get; set; } 
    public string SortOrder { get; set; } 
} 

XAML

<UserControl.Resources>  
    <DataTemplate DataType="{x:Type vm:ItemDetail}"> 
     <Grid> 
      <Grid.ColumnDefinitions> 
       <ColumnDefinition SharedSizeGroup="CheckBoxGroup" /> 
       <ColumnDefinition SharedSizeGroup="ItemNameGroup" /> 
       <ColumnDefinition SharedSizeGroup="SortGroup" /> 
       <ColumnDefinition Width="20" /> 
       <ColumnDefinition SharedSizeGroup="UpArrowGroup" /> 
       <ColumnDefinition SharedSizeGroup="DownArrowGroup" /> 
       <ColumnDefinition Width="*" /> 
      </Grid.ColumnDefinitions> 
      <CheckBox Grid.Column="0" IsChecked="{Binding IsSelected}" VerticalAlignment="Center" /> 
      <Label Grid.Column="1" Content="{Binding ItemName}" /> 
      <ComboBox Grid.Column="2" ItemsSource="{Binding DataContext.SortList, RelativeSource={RelativeSource AncestorType={x:Type views:ListBoxExample}}}" SelectedItem="{Binding SortOrder}" /> 
      <Button Grid.Column="4" Command="{Binding DataContext.UpCommand, RelativeSource={RelativeSource AncestorType={x:Type views:ListBoxExample}}}" CommandParameter="{Binding}"> 
       <Image Source="..\images\up.png" Height="10" /> 
      </Button> 
      <Button Grid.Column="5" Command="{Binding DataContext.DownCommand, RelativeSource={RelativeSource AncestorType={x:Type views:ListBoxExample}}}" CommandParameter="{Binding}"> 
       <Image Source="..\images\down.png" Height="10" /> 
      </Button> 
     </Grid> 
    </DataTemplate> 
</UserControl.Resources> 

<Grid Grid.IsSharedSizeScope="True"> 
    <ItemsControl ItemsSource="{Binding ItemDetails}" /> 
</Grid> 

最後的結果:

enter image description here

,然後按第一項的下拉箭頭後:

enter image description here

希望這有助於。

+0

這就是我要做的。 – mdm20 2013-02-26 17:58:45

+0

+1因爲你做得比我更快=) – 2013-02-26 17:59:48

+0

感謝您的詳細解答。請問RelayCommand有什麼功能? – 2013-02-26 18:37:51