2013-02-26 81 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