Databinding in xaml SelectedItem - c#

Please have a look at this:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
DataContext="{StaticResource vm}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="194*"/>
<ColumnDefinition Width="489*"/>
</Grid.ColumnDefinitions>
<ListView HorizontalAlignment="Left"
ItemsSource="{Binding Path=Places}"
SelectedItem="{Binding Path=SelectedPlace, Mode=TwoWay}" Margin="0,96,0,0">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Title}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackPanel Grid.Column="0" HorizontalAlignment="Left">
<TextBlock
VerticalAlignment="Top"
Text="{Binding SelectedPlace.Title}" Margin="0,64,0,0"/>
</StackPanel>
</Grid>
I am passing in list of Places, its working fine and the list gets displayed in the view. The problem is the selectedItem. The intellisense finds the property here
Text="{Binding SelectedPlace.Title}"
But it does not show up in the view.
When I put a breakpoint in my viewmodel, I can see that that value changes:
public class MainViewModel : ViewModelBase
{
public ObservableCollection<Place> Places { get; set; }
public Place _selectedPlace { get; set; }
public Place SelectedPlace
{
get { return _selectedPlace; }
set { _selectedPlace = value; }
}
public MainViewModel()
{
Places = new ObservableCollection<Place>()
{
new Place() {Title = "London", Description = "London is a nice..."},
new Place() {Title = "Dublin", Description = "Dublin is a ...."}
};
}
}
Does anyone know what i am missing? Thanks

You need to invoke RaisePropertyChanged.
public Place SelectedPlace
{
get { return _selectedPlace; }
set
{ _selectedPlace = value;
RaisePropertyChanged("SelectedPlace")}
}
}
And you should probably also initialize this property:
public MainViewModel()
{
Places = new ObservableCollection<Place>()
{
new Place() {Title = "London", Description = "London is a nice..."},
new Place() {Title = "Dublin", Description = "Dublin is a ...."}
};
SelectedPlace = Places[0];
}
And do yourself a favor by making the backing field of the SelectedPlace property a private field. You probably want to change it as this:
public Place _selectedPlace { get; set; }
to
private Place _selectedPlace;

Related

Changing the ViewModel's property from another ViewModel

I have the ListBox on my MainView.xaml, selecting the Item forces the ContentControl to display different UserControls. I use Caliburn.Micro library in this propgram. Here's some code:
<ListBox Grid.Row="1" Grid.Column="1" x:Name="ItemsListBox" SelectedItem="0" ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding TextBlock1Text}" x:Name="TextBlock1"/>
<ContentControl Grid.Row="3" Grid.Column="1" Content="{Binding ElementName=ItemsListBox, Path=SelectedItem.Content}" />
The MainViewModel.cs:
private string _name;
public string Name
{
get => _name;
set
{
_name = value;
NotifyOfPropertyChange(() => Name);
}
}
private string _textBlock1Text;
public string TextBlock1Text
{
get => _textBlock1Text;
set
{
_textBlock1Text = value;
NotifyOfPropertyChange(() => TextBlock1Text);
}
}
public MainViewModel()
{
TextBlock1Text = "Test";
Items = new ObservableCollection<ItemsModel>()
{
new ItemsModel { Name="Useless", Content=null },
new ItemsModel { Name="TextChangerViewModel", Content=new TextChangerViewModel(TextBlock1Text) }
};
}
public ObservableCollection<ItemsModel> Items { get; set; }
The ItemsModel.cs:
public class ItemsModel
{
public string Name { get; set; }
public object Content { get; set; }
}
And finally the TextChangerViewModel.cs:
public class TextChangerViewModel : Conductor<object>
{
private string _textBlock1Text;
public string TextBlock1Text
{
get => _textBlock1Text;
set
{
_textBlock1Text = value;
NotifyOfPropertyChange(() => TextBlock1Text);
}
}
public TextChangerViewModel(string textBlock1Text) //passing parameter from another ViewModel
{
TextBlock1Text = textBlock1Text;
}
}
So, the main question is how to change the TextBlock1Text (and the Text value of TextBlock in .xaml as well) in the MainViewModel.cs from the TextChangerViewModel.cs? I was thinking about using something like NotifyCollectionChanged on my Items ObservableCollection, but it work with collection of ItemsModel, not with the VM's, so I'm stuck here.
I'm also not sure if having public object Content { get; set; } in ItemsModel.cs is a good thing if I'm targeting the MVVM pattern, but I don't know the other way to do it (I'm very new to MVVM).
UPD
I'm looking for the property-changing way because I need to change the TextBlock1Text Text from another UserControl. Suppose I have the button on my TextChangerView.xaml: <Button Grid.Row="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Content="Change da text" cal:Message.Attach="ChangeTextButton"/>
And after the click on it I want the text on the parental MainView.xaml to change. But the thing is, I don't know how to change properties in this case, as I wrote above why.
Change the the binding of textblox1 to reference the selected item.
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding ElementName=ItemsListBox, Path=SelectedItem.Name}" x:Name="TextBlock1"/>
or
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding ElementName=ItemsListBox, Path=SelectedItem.Content.TextBlock1Text}" x:Name="TextBlock1"/>

Not able to see binded items to listbox

I've created a ListBox with this structure:
<ListBox VerticalAlignment="Stretch"
Background="AliceBlue"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ItemsSource="{Binding EventInfo}">
how you can see I binded the EventInfo property that I valorize behind code. This property have the OnPropertyChange(); implementation as my other properties, and the value setted is got correctly. Anyway, I'm not able to display the binded source:
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=League}" />
<TextBlock Text="test" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
now the property League value isn't displayed also the value test. I really don't understand why. The League property exist, and also I've no error in xaml.
What I did wrong?
UPDATE:
public Models.EventInfo EventInfo
{
get { return _eventInfo; }
set
{
_eventInfo = value;
OnPropertyChanged();
}
}
and in the Model
public class EventInfo
{
public string League { get; set; }
public string Date { get; set; }
public string GameWeek { get; set; }
public string GameStart { get; set; }
public string FirstTime { get; set; }
public string SecondTime { get; set; }
public string Stadium { get; set; }
public List<MatchArbiter> Arbiter { get; set; }
}
Try this. You need to populate ItemsSource with a collection, not a single item. Instead of your existing EventInfo property, you need a collection property. I'm going to rename it to EventInfoItems to keep confusion to a minimum.
private ObservableCollection<Models.EventInfo> _eventInfoItems =
new ObservableCollection<Models.EventInfo>();
public ObservableCollection<Models.EventInfo> EventInfoItems
{
get { _eventInfoItems; }
set
{
_eventInfoItems = value;
OnPropertyChanged();
}
}
Now, somewhere, you're going to have to add some items to that collection if you want anything to appear in the list. You could create a few test items in your viewmodel constructor, just for the time being. Like this:
EventInfoItems.Add(new EventInfo { League = "NBA" });
EventInfoItems.Add(new EventInfo { League = "Premier League" });
EventInfoItems.Add(new EventInfo { League = "Serie A" });
XAML
<ListBox
VerticalAlignment="Stretch"
Background="AliceBlue"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ItemsSource="{Binding EventInfoItems}"
>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=League}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
Update
Turns out OP may have only one item. If that's the case, a ListBox is unnecessary. A ContentControl is the right control when you've got only one item and you want to display it with a DataTemplate. This XAML will use the original version of the EventInfo property:
public Models.EventInfo EventInfo
{
get { return _eventInfo; }
set
{
_eventInfo = value;
OnPropertyChanged();
}
}
XAML:
<ContentControl
VerticalAlignment="Stretch"
Background="AliceBlue"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Visible"
Content="{Binding EventInfo}"
>
<ContentControl.ContentTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=League}" />
</StackPanel>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>

MVVM WPF Data Binding issue

I am new to MVVM and WPF, I have the below model which I want to show it to user using the data binding (Code for viewmodel and xaml is also provided below). But I am not sure what is missing, because the list of users are not being shown on UI at all. Can anyone tell me which I am missing from my code?!
If instead Class of Users, I use a List from class User:
private List<User> _UsersList;
public List<User> users
{
get { return _UsersList; }
set { _UsersList = value; }
}
, then the biding works!, but if I use Users class, the binding does not work!
Model:
public class User : ObservableObject
{
public int ID { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public bool IsPremiumUser { get; set; }
public string selectedItem { get; set; }
public string SelectedValue
{
get { return selectedItem; }
set
{
selectedItem = value;
// suser.age = Convert.ToInt32(value);
RaisePropertyChangedEvent("SelectedValue");
}
}
public int[] myItem { get; set; }
public int[] UserItems
{
get { return myItem; }
set { myItem = value; }
}
private SelectedUser suser = new SelectedUser();
public int selected { get; set; }
public int Selected
{
get { return selected; }
set
{
selected = value;
suser.age = Convert.ToInt32(value);
RaisePropertyChangedEvent("Selected");
}
}
}
public class Users : ObservableObject
{
private List<User> mylist = new List<User>();
private List<User> list
{
get { return mylist; }
set
{
mylist = value;
}
}
public void Add(User user)
{
this.list.Add(user);
}
public User GetById(int id)
{
return this.list.First(u => u.ID == id);
}
}
ViewModel:
public class ViewModel : ObservableObject
{
public ViewModel()
{
users = new Users();
for (int i = 0; i < 100000; ++i)
{
int[] numbers;
numbers = new int[3] { 1, 2, 3 };
var user = new User { ID = i, Name = "Name " + i.ToString(), Age = 20 + i, UserItems = numbers, SelectedValue = "0" };
if (i == 2 || i == 4)
{
user.IsPremiumUser = true;
}
users.Add(user);
}
}
private Users _UsersList;
public Users users
{
get { return _UsersList; }
set { _UsersList = value; }
}
private int _currenuser;
public int CurrentUser
{
get
{
return _currenuser;
}
set
{
_currenuser = value;
RaisePropertyChangedEvent("CurrentUser");
}
}
}
XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Icon="C:\Users\maninx2\Documents\Visual Studio 2013\Projects\SampleFormMaker1\WpfApplication1\abbott_point_of_care_HuT_icon.ico"
Title="MainWindow" Height="700" Width="1249.129">
<Window.Resources>
<DataTemplate x:Key="NormalUserDataTemplate">
<StackPanel>
<TextBlock Text="{Binding Name}" />
<TextBox Text="{Binding SelectedValue, UpdateSourceTrigger=PropertyChanged}" Width="300" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="PremiumUserDataTemplate">
<StackPanel Background="LightBlue">
<ComboBox SelectedIndex="{Binding SelectedValue, UpdateSourceTrigger=PropertyChanged}" Text="{Binding Name}" Width="300" ItemsSource="{Binding UserItems}"/>
</StackPanel>
</DataTemplate>
<local:PremiumUserDataTemplateSelector x:Key="myPremiumUserDataTemplateSelector" />
</Window.Resources>
<TabControl Name="TabControl1">
<TabControl.ItemContainerStyle>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Visibility" Value="Collapsed"/>
</Style>
</TabControl.ItemContainerStyle>
<TabItem Header="General">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Height="800" Orientation="Horizontal" >
<ListView x:Name="myListView" ItemsSource="{Binding users, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" ItemTemplateSelector="{StaticResource myPremiumUserDataTemplateSelector}" SelectedIndex="{Binding CurrentUser, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
</ListView>
<ListView x:Name="myListView1" ItemsSource="{Binding users, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Stretch" >
<ListView.View>
<GridView>
<GridViewColumn Width="140" Header="Selected Values"
DisplayMemberBinding="{Binding SelectedValue}" />
</GridView>
</ListView.View>
</ListView>
<Button Content="Next"
HorizontalAlignment="Left"
Margin="10,10,0,0"
VerticalAlignment="Top"
Width="75"
Click="Button_Click"/>
</StackPanel>
</ScrollViewer>
</TabItem>
<TabItem Header="Second Tab">
<StackPanel>
<TextBlock Name="lbl1" Text="{Binding CurrentUser, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="Previous"
HorizontalAlignment="Left"
Margin="10,10,0,0"
VerticalAlignment="Top"
Width="75"
Click="Button_Click1"/>
</StackPanel>
</TabItem>
</TabControl>
</Window>
You are missing inheriting the Users from ObservableCollection. So, WPF engine does not know how to get the data from this class.
Update:
#user3033921: You inherited it from ObservableObject not ObservableCollection. So, the thing is if you want this class to be recognized from as a list then you would have to get that class inherited by an ICollection object and if you want that class to be observable it should be implementing both ICollection and INotifyPropertyChange. So, to #BradleyDotNet's point if you dont have any specific reason to create your own type, then just create a object of users with type ObservableCollection. If you have a specific need to have a type Users then you can derive it from ObserableCollection as recommended solution. But keep in mind you do not want to have List in the Users class anymore as your class is now by itself is a list. So, if you have any specific implementation to do like GetByID, then the class would look like:
public class Users :ObservableCollection<User>
{
public User GetById(int id)
{
return this.First(u => u.ID == id);
}
}

Best approach to add an item to a databound Listbox using MVVM pattern?

I'm facing a problem in my WPF project at the moment. At this moment I have a Viewmodel which has a Manager (to communicate with the repo).
internal class TicketViewModel
{
private TicketManager mgr;
public IEnumerable<Ticket> List { get; set; }
public TicketViewModel()
{
mgr = new TicketManager();
List = mgr.GetTickets();
}
}
I've managed to bind this list to the Listbox in my MainWindow. The next step is that I need to add an extra ticket to the list and also pass this through the manager. The problem is I need two parameters from some Controls in the MainWindow. From MVVM perspective I need to use bound Commands on e.g. a Button to communicate with the viewmodel as my viewmodel can't/may not access controls from the window. Is using parameterized Commands the way to go here?
The next problem is that the Listbox won't update I guess. This is the code:
<ListBox x:Name="listboxTix" BorderThickness="0" ItemsSource="{Binding List}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Bisque" Background="Beige" BorderThickness="2">
<StackPanel Width="250">
<TextBlock Text="{Binding TicketNumber}" />
<TextBlock Text="{Binding Text}" />
<TextBlock Text="{Binding State}" />
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I found that using a CompareableCollection is the way to go here, but then I still have to read all the Tickets again after adding a new Ticket.
Thanks in advance,
Hicy
okay here is the code.
Lets say you have three textboxes on MainWindow(since you have three Textblocks.) so Your MainWindow.xaml looks like
<Window.DataContext>
<local:MyViewModel/>--set's your viewModel
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="250*"/>
<RowDefinition Height="90"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" x:Name="listboxTix" BorderThickness="0" ItemsSource="{Binding List}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Bisque" Background="Beige" BorderThickness="2">
<StackPanel Width="250">
<TextBlock Text="{Binding TicketNumber}" />
<TextBlock Text="{Binding Text}" />
<TextBlock Text="{Binding State}" />
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBox x:Name="TicketNumber" Grid.Row="1" TextWrapping="Wrap" Text="{Binding Path=Text}" VerticalAlignment="Top" Width="120"/>
<TextBox x:Name="Text" Grid.Row="1" TextWrapping="Wrap" Text="{Binding Path=State}" />
<TextBox x:Name="State" Grid.Row="1" TextWrapping="Wrap" Text="{Binding Path=TicketNumber}" />
<Button Content="Button" Command="{Binding Path=MainCommand}" Grid.Row="2"/>
</Grid>
and I am assuming that you have some class called class Ticket which contain these three members
Class Ticket
{
public int TicketNumber { get; set; }
public string Text { get; set; }
public string State { get; set; }
}
Now in class TicketManager we fill it with some dummy data
class TicketManager
{
ObservableCollection<Ticket> tl = new ObservableCollection<Ticket>();
internal ObservableCollection<Ticket> GetTickets()
{
tl.Add(new Ticket() { State = "State1", Text = "Text1", TicketNumber = 1 });
tl.Add(new Ticket() { State = "State2", Text = "Text2", TicketNumber = 2 });
tl.Add(new Ticket() { State = "State3", Text = "Text3", TicketNumber = 3 });
return tl;
}
}
and in your Mainwindow ViewModel lets call it MyViewModel.cs we add
class MyViewModel:INotifyPropertyChanged
{
private TicketManager mgr;
public ObservableCollection<Ticket> List { get; set; }
private string text;
private string state;
private int ticketNumber;
private readonly DelegateCommand<object> MyButtonCommand;
public Class1()
{
mgr = new TicketManager();
List = mgr.GetTickets();
MyButtonCommand = new DelegateCommand<object>((s) => { AddListToGrid(text, state, ticketNumber); }, (s) => { return !string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(state); });
}
private void AddListToGrid(string text, string state, int ticketNumber)
{
List.Add(new Ticket() {Text=text,State=state,TicketNumber=ticketNumber });
}
public DelegateCommand<object> MainCommand
{
get
{
return MyButtonCommand;
}
}
public string Text
{
get
{
return text;
}
set
{
text = value;
OnPropertyChanged("Text");
MyButtonCommand.RaiseCanExecuteChanged();
}
}
public string State
{
get
{
return state;
}
set
{
state = value;
OnPropertyChanged("State");
MyButtonCommand.RaiseCanExecuteChanged();
}
}
public int TicketNumber
{
get
{
return ticketNumber;
}
set
{
ticketNumber = value;
OnPropertyChanged("TicketNumber");
MyButtonCommand.RaiseCanExecuteChanged();
}
}
private void OnPropertyChanged(string p)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
public event PropertyChangedEventHandler PropertyChanged;
}
You can Modify the code in anyway you want
This ViewModel implements fewthings which are very important from MVVM point of view
1) INotifyPropertyChanged
2) WPF Delegate Command
P.S:The code is tested and it runs as expected
Don't get hung up on MVVM it is simply a separation of data from a view, and models are shared between the two with a majority of the business logic (on a shared component) should be performed on the VM; it is not a religion just a three tiered data system. IMHO
If your button needs to do an operation, have it make a call, most likely in the code behind, to a method on the VM which handles the business logic, updates the list with the new item and notifies the manager.
I would bind the list in question to an ObservableCollection which can notify upon insert/delete of an item.

How to move items in a Grid

I've got a WPF Grid and would like to move rows up or down depending on the user's input. This is what I've tried so far (an example for when the user decides to move an element up):
RowDefinition currentRow = fieldsGrid.RowDefinitions[currentIndex];
fieldsGrid.RowDefinitions.Remove(currentRow);
fieldsGrid.RowDefinitions.Insert(currentIndex - 1, currentRow);
Am I doing something wrong? As the UI remains the same using this approach.
This would be the WPF approach to what you're screenshot looks like:
<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>
Code Behind:
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}
}
This is what it looks like in my screen:
As you can see in the above example, I am in no way manipulating or creating UI elements in code, because it's actually not necessary. Whenever you need to interact with the pieces of information displayed in the screen, you interact with the ViewModels and not the View. This is the clear separation of concerns between UI and application logic WPF makes possible, which is utterly absent in other frameworks. Please consider this approach the de-facto default when doing any kind o N-element UIs in WPF.
Edit:
Advantages of this approach versus the classic one:
No need to manipulate complex WPF classes (I.E UI elements) in your code in order to show / get data from screen (just simple, simple
properties and INotifyPropertyChanged)
Scales better (UI can be anything as long as it honors the ViewModel properties, you could change the ComboBox to a rotating 3d
pink elephant with a Sort order in each foot.
No need to navigate the visual tree to find elements located God knows where.
No need to foreach anything. Just a simple Select that converts your data (from whatever data source you obtained it) to the
ViewModel list.
Bottom line: WPF is much simpler and nicer than anything else currently in existence, if you use the WPF approach.
Here is a quick example of using an ItemsControl to do what you are wanting:
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 (Made up by me to make things easier)
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>
And finally the results:
And after pressing the down arrow on the first item:
Hope this helps.
You are changing the order of the RowDefinitions, which is not what you want. You want to change the assignment of elements to rows, which is determined by the Grid.Row attached property
I would put all controls that belong to each row in a container (one per row) and then use Grid.SetRow to change the containers around. See how to change the grid row of the control from code behind in wpf.

Categories

Resources