我创建了一个看起来像瓷砖的用户控件。创建了另一个名为TilePanel的用户控件,用作tile的默认容器。最后,看起来像Window开始屏幕的UI。我使用RelayCommand来绑定我的TileCommands
以下是代码:
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用户控件作为tile的容器
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; }
}
}
我的TilePanel ViewModel
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"); }
}
}
}
这是我的StartScreen视图,其中应显示带有图块的TilePanel ...
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 并使用 Tile.Add(tile); 在TilePanel中使用Tiles填充我的开始屏幕...
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);
现在问题是,如果我在下面添加新代码,则tile = new TileModel(){...} tile.TileCommand = new RelayCommand(...),即使我点击了第一个图块,我的Tile_TileClick()也会获得第二个图块的信息(或插入的最后一个图块)......
我做错了吗?或者我做错了什么......?
答案 0 :(得分:1)
这不是你问题的直接答案,但希望它会给你一些想法。
好的,首先,不要像这样命名你的用户控件:
<UserControl x:Class="MyNamespace.Tilev2" Name="Tile"/>
因为在某处使用usercontrol时可以轻松覆盖名称:
<local:Titlev2 Name="SomeOtherName" />
并且Tilevs与ElementName之间的绑定不起作用:Command="{Binding ElementName=Tile, Path=TileClickCommand}"
其次,Tilev2
usercontrol的重点是什么?为什么不直接将按钮放在TilePanel类内的DataTemplate中?
如果您需要重复使用模板,可以将模板放到资源字典中。
如果您需要Tilev2
代码隐藏中的某些特殊演示代码,或者您需要使用Tilev2
而不使用viewmodel,那么最好创建自定义控件而不是usercontrol 强>在这种情况下。它有更好的设计时间支持,并且编写控件模板更容易(Triggers,DataTriggers,TempalteBinding等)。如果您使用自定义Control insead UserControl,则不必编写{Binding ElementName=Tile, Path=TileClickCommand}
,或使用RelativeSource等。
第三,似乎你强制MVVM模式,你可以真正利用它。 MVVM的点是与表示不同的应用程序逻辑。但是你的Tile和TilePanel用户控件只是演示文稿。您的应用程序逻辑可以在StartScreen中,这是TileName的具体用法。
我会创建名为TilePanel的自定义控件(可能继承自ItemsControl,Selector或ListBox),如果需要也可以用于Tile。两个控件都不应该知道任何视图模型。绝对没有必要这样做。
以ListBox为例。 ListBox没有viewmodel,但可以在MVVM场景中轻松使用。仅仅因为ListBox没有绑定到任何视图模型,它可以被数据绑定到任何东西。
就像ListBox创建ListBoxItems或者
一样
Combobox创建ComboBoxItems,或者
DataGrid创建DataGridRows或
GridView(在WinRT中)创建GridViewRow,你的TilePanel可以创建Tiles。
可以在TilePanel.ItemContainerStyle中指定图块特定属性(如图标或命令)的绑定,也可以在ListBox中使用类似DisplayMemberPath,resp ValueMemberPath的simillar appriach。
最终用法可能如下:
<TilePanel ItemsSource="{Bidning ApplicationTiles}" />
或
<TilePanel>
<Tile Icon=".." Command=".." Text=".." />
<Tile Icon=".." Command=".." Text=".." />
</TilePanel>
最后,名称`TilePanel&#39;诱发它是某种类型的面板,如StackPanel,WrapPanel等。换句话说,它是继承自Panel的FrameworkElement。
TilesView比TilePanel更适合控件的名称。 -View后缀不是来自MVVM,它只遵循命名约定-GridView,ListView ...
答案 1 :(得分:0)
看到问题......
为了从按钮传递参数,我使用了CommandParameter
,因此我可以在switch-case场景中使用它来知道单击了哪个按钮。但是,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)}
在整整两天之后,我改变了它:
由此:
<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获取任何内容。