2013-02-26 3 views
1

У меня есть WPF Grid и вы хотите перемещать строки вверх или вниз в зависимости от ввода пользователя. Это то, что я пытался до сих пор (пример, когда пользователь решает переместить элемент вверх):Как перемещать элементы в сетке

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

Я делаю что-то не так? Поскольку пользовательский интерфейс остается неизменным с использованием этого подхода.

+1

'У меня есть WPF Grid и вы хотите перемещать строки вверх или вниз в зависимости от ввода пользователя' - Для чего вам это нужно? пожалуйста, уточните свое намерение, потому что, вероятно, есть намного лучший и простой способ сделать то, что вам нужно. –

+0

Это звучит странно и неправильно. Если у вас есть набор данных, которые нужно перемещать на основе ввода пользователем, тогда вы должны использовать элементы управления, которые позволяют это легко, то есть Listbox, Datagrid и т. Д. –

+0

Я отредактировал вопрос, чтобы добавить некоторый контекст @BrentStewart –

ответ

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; 
     } 
    } 
} 

ViewModel:

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

Как вы можете видеть в приведенном выше примере, я никоим образом не манипулирую или не создаю элементы интерфейса в коде, потому что на самом деле это не нужно. Всякий раз, когда вам нужно взаимодействовать с фрагментами информации, отображаемыми на экране, вы взаимодействуете с ViewModel s, а не с View. Это четкое разделение проблем между пользовательским интерфейсом и логикой приложения, что позволяет WPF, который совершенно отсутствует в других рамках. Пожалуйста, рассмотрите этот подход де-факто по умолчанию при выполнении каких-либо действий. U-интерфейсы N-элемента в WPF.

Edit:

Преимущество такого подхода по сравнению с classic один:

  • Нет необходимости манипулировать сложные классы WPF (IE элементы пользовательского интерфейса) в своем коде, чтобы показать/получить данные с экрана (просто, просто свойств и INotifyPropertyChanged)
  • Весы лучше (UI может b e до тех пор, пока он почитает свойства ViewModel, вы можете изменить ComboBox на вращающийся 3d слон-слон с порядком сортировки в каждой ноге.
  • Не нужно перемещаться по визуальному дереву, чтобы найти элементы, находящиеся там, где Бог знает, где.
  • Нет необходимости foreach ничего. Просто простой Select, который преобразует ваши данные (из любого источника данных, который вы получили) в список ViewModel.

Итог: WPF гораздо проще и приятнее, чем все остальное в настоящее время существует, если вы используете подход WPF.

+0

@DotNet Im, использующий метод NotifyPropertyChange, основанный на выражении (http://stackoverflow.com/questions/2711435/typesafe-notifypropertychanged-using-linq-expressions) в классе ViewModelBase. –

+0

@DotNET как комментарий в стороне, всегда лучше делать все правильно, потому что в конце концов это помогает вам достичь мертвых линий и еще много чего. Возьмите секунду, чтобы проанализировать, как бы вы это сделали, чтобы перечитать все данные из элементов пользовательского интерфейса, которые вы создали в коде, скажем, после того, как пользователь нажимает «Сохранить» или что-то в этом роде. –

+0

Я согласен с вами полностью в HighCore :) Единственное, что, если бы мне пришлось изменить эту часть приложения, большая часть очень тяжелого кода должна была идти. Я должен взвесить плюсы и минусы, я думаю, –

1

Вы меняете порядок RowDefinition s, который не является тем, что вы хотите. Вы хотите изменить назначение элементов в строках, которое определяется Grid.Row attached property

Я бы поместил все элементы управления, принадлежащие каждой строке в контейнере (по одному на строку), а затем с помощью Grid.SetRow, чтобы изменить контейнеры вокруг , См. how to change the grid row of the control from code behind in wpf.

+0

Проблема в том, что я хочу, чтобы строки были правильно выровнены, но –

+0

Затем вы должны либо перебирать элементы управления в сетке, либо вызывать Grid.SetRow, либо использовать что-то еще (например, ListBox или DataGrid, как это предлагается в комментарии Брент Стюарт) – sinelaw

+0

Добавлен код - можете ли вы порекомендовать элемент управления? –

3

Вот краткий пример использования ItemsControl делать то, что вы хотели:

ViewModel

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 Class (составляемую мной, чтобы сделать вещи проще)

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

+0

+1 Потому что вы сделали это быстрее меня =) –

+0

Спасибо за подробный ответ. Могу ли я спросить, что делает RelayCommand? –

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