2016-03-11 2 views
0

Я создал пользовательский элемент управления, который выглядит как плитка. Создал другой пользовательский элемент управления с именем TilePanel, который служит контейнером по умолчанию для плиток. И, наконец, сам интерфейс, который выглядит как экран запуска окна. Я использовал RelayCommand связать мои TileCommandsRelayCommand не получает правильную модель

Здесь представлены коды:

Tilev2.xaml

<UserControl x:Class="MyNamespace.Tilev2" 
      Name="Tile".... 
> 
... 
    <Button x:Name="btnTile" Style="{StaticResource TileStyleButton}" Command="{Binding ElementName=Tile, Path=TileClickCommand}" > 
    </Button> 

</UserControl> 

Tilev2.xaml.cs

public partial class Tilev2 : UserControl 
    { 
     public Tilev2() 
     { 
      InitializeComponent(); 
     } 
//other DPs here 
     public ICommand TileClickCommand 
     { 
      get { return (ICommand)GetValue(TileClickCommandProperty); } 
      set { SetValue(TileClickCommandProperty, value); } 
     } 

     // Using a DependencyProperty as the backing store for TileClickCommand. This enables animation, styling, binding, etc... 
     public static readonly DependencyProperty TileClickCommandProperty = 
      DependencyProperty.Register("TileClickCommand", typeof(ICommand), typeof(Tilev2)); 
    } 
    } 

Затем я создал Пользовательское управление TilePanel как контейнер плитки

TilePanel.xaml

<UserControl x:Class="MyNamespace.TilePanel" 
      ... 
      > 
    <Grid> 
     <ScrollViewer> 
      <ItemsControl Name="tileGroup" 
          ItemsSource="{Binding TileModels}" > 
       <ItemsControl.ItemsPanel> 
        <ItemsPanelTemplate> 
         <WrapPanel Orientation="Horizontal"/> 
        </ItemsPanelTemplate> 
       </ItemsControl.ItemsPanel> 
       <ItemsControl.ItemTemplate> 
        <DataTemplate> 
         <local2:Tilev2 TileText="{Binding Text}" 
              TileIcon="{Binding Icon}" 
              TileSize="{Binding Size}" 
              TileFontSize="{Binding FontSize}" 
              Background="{Binding Background}" 
              TileCaption="{Binding TileCaption}" 
              TileCaptionFontSize="{Binding TileCaptionFontSize}" 
              TileClickCommand="{Binding TileCommand}" 
              /> 
        </DataTemplate> 
       </ItemsControl.ItemTemplate> 
      </ItemsControl> 
     </ScrollViewer> 
    </Grid> 
</UserControl> 

TilePanel.xaml.cs

public partial class TilePanel : UserControl 
{ 
    public TilePanel() 
    { 
     InitializeComponent(); 
     DataContext = new TilePanelViewModel(); 
    } 

    public TilePanelViewModel ViewModel 
    { 
     get { return (TilePanelViewModel)this.DataContext; } 
    } 
} 

Мои ViewModel для TilePanel

TilePanelViewModel.cs

public class TilePanelViewModel: ViewModelBase { private ObservableCollection _tileModels;

public ObservableCollection<TileModel> TileModels 
{ 
    get 
    { 
     if (_tileModels == null) 
      _tileModels = new ObservableCollection<TileModel>(); 

     return _tileModels; 
    } 
} 

}

Тогда моя плитка модель

TileModel.cs

public class TileModel : BaseNotifyPropertyChanged 
    { 
     //other members here 
     ICommand tileCommand { get; set; } 
//other properties here 
public ICommand TileCommand 
     { 
      get { return tileCommand; } 
      set { tileCommand = value; NotifyPropertyChanged("TileCommand"); } 
     } 
    } 
} 

Это мой Старт.экр View, где должны отображаться TilePanels с плиткой ...

StartScreen.xaml

<UserControl x:Class="MyNamespace.StartMenu" 
      ... > 
<Grid> 
     <DockPanel x:Name="dockPanel1" Grid.Column="0" Grid.Row="1" Margin="50,5,2,5"> 
      <local:TilePanel x:Name="tilePanel"></local:TilePanel> 
     </DockPanel> 
</Grid> 
</UserControl> 

StartScreen.xaml.cs

public partial class WincollectStartMenu : UserControl, IView<StartMenuViewModel> 
    { 
     public WincollectStartMenu() 
     { 
      InitializeComponent(); 
     } 
public StartMenuViewModel ViewModel { get { return (DataContext as StartMenuViewModel); } } 

     private void UserControl_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e) 
     { 
      ViewModel.Tile = tilePanel.ViewModel.TileModels; 
     } 

     private void UserControl_Loaded(object sender, RoutedEventArgs e) 
     { 
      return; 
     } 
} 

В моем начальном экране ViewModel, я использовал ObservableCollection Плитка и использовать Tile.Add (плитка); заполнить свой стартовый экран с плитками внутри TilePanel ...

StartMenuViewModel.cs

TileModel tile = new TileModel() { Text = "Testing1", FontSize = 11, Size = TileSize.Medium, Background = (SolidColorBrush)new BrushConverter().ConvertFromString("#039BE5"), Tag="Something" }; 
    tile.TileCommand = new RelayCommand(
      p => Tile_TileClick(tile.Tag), 
      p => true 
     ); 
    temp.Add(tile); 

Теперь проблема, если добавить новый код ниже, плитка = новый TileModel() { ...} tile.TileCommand = new RelayCommand (...), даже если бы я нажал на первую плитку, мой Tile_TileClick() получит информацию второй плитки (или вставил последнюю плитку) ...

Я что-то не так? Или я все делаю неправильно ...?

ответ

0

Saw the problem ... Чтобы передать параметр с помощью кнопки, я использовал CommandParameter, поэтому я мог использовать его в сценарии с коммутатором, чтобы узнать, какая кнопка была нажата. Но все-таки, param еще был нулевой ...

<Button x:Name="btnTile" Style="{StaticResource TileStyleButton}" CommandParameter="{Binding}" Command="{Binding Path=TileClickCommand, ElementName=Tile}" > 
     </Button> 

TileCommand = new MyCommand() { CanExecuteFunc = param => CanExecuteCommand(), ExecuteFunc = param => Tile_TileClick(param)} 

Через 2 целых проклятых дней, я изменил его:

Отсюда:

<UserControl Name="Tile"...> 
    <Button x:Name="btnTile" Style="{StaticResource TileStyleButton}" CommandParameter="{Binding Tag, ElementName=Tile}" Command="{Binding Path=TileClickCommand, ElementName=Tile}" > 
    </Button> 
</UserControl> 

Для этого:

<UserControl Name="Tile"...> 
    <Button x:Name="btnTile" Style="{StaticResource TileStyleButton}" CommandParameter="{Binding}" Command="{Binding Path=TileClickCommand, ElementName=Tile}" > 
    </Button> 
</UserControl> 

Мое первое сообщение делает ошибку, потому что CommandParameter не знает, где получить свой DataContext, поэтому я реплику запустил его до CommandParameter={Binding}, чтобы он получал все, что от DataContext.

1

Это не прямой ответ на ваш вопрос, но, надеюсь, он даст вам несколько мыслей.

Ok, в первую очередь, не называйте свой UserControl вроде этого:

<UserControl x:Class="MyNamespace.Tilev2" Name="Tile"/> 

, так как имя может быть легко перекрываться при использовании UserControl где-то:

<local:Titlev2 Name="SomeOtherName" /> 

и связывание внутри Tilevs с ElementName не будет работать: Command="{Binding ElementName=Tile, Path=TileClickCommand}"


Во-вторых, в чем смысл Tilev2 usercontrol? Почему бы просто не поместить кнопку непосредственно в DataTemplate внутри класса TilePanel?

Если вам нужно повторно использовать шаблон, вы можете поместить шаблон в словарь ресурсов.

Если вам нужен какой-то специальный код представления в Tilev2 коде или вам нужно использовать Tilev2 без ViewModel, то лучше создать пользовательский элемент управления вместо UserControl в этом случае. он имеет гораздо лучшую поддержку времени разработки, а писать шаблоны управления проще (триггеры, DataTriggers, TempalteBinding и т. д.). Если вы использовали пользовательский элемент управления в INSEAD UserControl, вам не придется писать {Binding ElementName=Tile, Path=TileClickCommand} или использовать RelativeSource и т.д.


В-третьих, кажется, что вы принудительному MVVM шаблон, в котором вы не можете воспользоваться этим. Точка MVVM - это отдельная логика приложения из презентации. Но ваши пользовательские настройки Tile и TilePanel - это просто презентация. Логика приложения может быть в StartScreen, которая является конкретным использованием TileName.

Я создал бы пользовательские элементы управления под названием TilePanel (желательно унаследованный от ItemsControl, Selector или ListBox) и, если необходимо, также для Tile.Оба элемента управления не должны знать о каких-либо моделях просмотра. Абсолютно нет необходимости в этом.

Возьмите ListBox в качестве примера. ListBox не имеет viewmodel, но может быть легко использован в сценариях MVVM. Просто потому, что ListBox не привязан к какой-либо viewmodel, он может быть привязкой к чему-либо.

Так же, как ListBox создает ListBoxItems или
выпадающий создает ComboBoxItems или
DataGrid создает DataGridRows или
GridView (в WinRT) создает GridViewRow, ваш TilePanel может создать плитки.

Привязки для конкретных свойств плитки, таких как Icon или Command, могут быть указаны в TilePanel.ItemContainerStyle или использовать simillar appriach, например DisplayMemberPath, resp ValueMemberPath в ListBox.

окончательное использование может внешний вид, как:

<TilePanel ItemsSource="{Bidning ApplicationTiles}" /> 

или

<TilePanel> 
    <Tile Icon=".." Command=".." Text=".." /> 
    <Tile Icon=".." Command=".." Text=".." /> 
</TilePanel> 

Последнее, название `TilePanel» вызвал, что это какой-то вид панели, как StackPanel, WrapPanel и т.д. Другими словами, это FrameworkElement, унаследованный от Panel.

TilesView будет более подходящим названием для управления, чем TilePanel. Постфикс -View не из MVVM, он просто следует за соглашением об именах -GridView, ListView ...

+0

Прежде всего, СПАСИБО ВАМ ТАК МОЖНО, чтобы указать на многие из моих ошибок. Я действительно очень ценю это. Я довольно новичок в WPF, не говоря уже о MVVM. Причина, почему я использую пользовательский контроль вместо пользовательского элемента управления, - это то, что я не слишком хорошо знаком со стилями. Если я буду оценивать себя, это будет 3 из 10, поэтому я продолжал использовать пользовательский контроль. На самом деле, спасибо за это. Я буду переконфигурировать свой пользовательский элемент управления на пользовательский элемент управления, используя ваши направляющие линии. Еще раз спасибо! –

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