I have been pounding my head on a table today trying to get my drag and drop events to fire. The drag and drop functionality works on the interface, but the events wont fire. I need the events to fire so I can update the database with the new order of the objects. What am I doing wrong?
In the code below, I place break points in the 'Drop' and 'DragOver' methods, but they never get hit.
XAML:
<Window x:Class="Reorder_item_WPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:dd="clr-namespace:GongSolutions.Wpf.DragDrop;assembly=GongSolutions.Wpf.DragDrop">
<Grid>
<ListBox Grid.Column="1" SelectionMode="Extended" ItemsSource="{Binding MSPCollection}"
dd:DragDrop.IsDragSource="True" Width="300" Margin="0,0,5,0" dd:DragDrop.IsDropTarget="True">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="#2ba3d5" Height="50" Width="280">
<TextBlock Text="{Binding Name}" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="40"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
c#:
public class MSP {
public int Id { get; set; }
public string Name { get; set; }
}
class MainViewModel : IDropTarget
{
public ObservableCollection<MSP> MSPCollection { get; set; }
public MainViewModel() {
MSPCollection = new ObservableCollection<MSP>();
MSPCollection.Add(new MSP() {
Id = 1,
Name = "Anis Derbel"
});
MSPCollection.Add(new MSP()
{
Id = 2,
Name = "Firas Mdimagh"
});
MSPCollection.Add(new MSP()
{
Id = 3,
Name = "Khaled Jemni"
});
MSPCollection.Add(new MSP()
{
Id = 4,
Name = "Sahbouch"
});
}
public void DragOver(IDropInfo dropInfo) {
if (dropInfo.Data is MSP) {
dropInfo.DropTargetAdorner = DropTargetAdorners.Highlight;
dropInfo.Effects = DragDropEffects.Move;
}
}
public void Drop(IDropInfo dropInfo) {
MSP msp = (MSP)dropInfo.Data;
((IList)dropInfo.DragInfo.SourceCollection).Remove(msp);
}
}
You also need to set the DropHandler via the respective Attached Property:
<ListBox ItemsSource="{Binding Collection}"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.DropHandler="{Binding}" />
From documentation
Related
I am trying to bind values to Combobox in a Windows 10 Universal App. I am using MVVM Light. I can bind all values properly except for Combo Box. The items are never shown. I am not sure what I am missing.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:BigappleSP.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ViewModels="using:BigappleSP.ViewModels"
x:Class="BigappleSP.Views.ContactusPage"
mc:Ignorable="d" DataContext="{Binding ContactusViewModdel, Source={StaticResource Locator}}">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<RelativePanel FlowDirection="LeftToRight" Height="640" VerticalAlignment="Bottom">
<TextBox x:Name="txtName" MinWidth="320"
PlaceholderText="Name" InputScope="PersonalFullName"
RelativePanel.AlignHorizontalCenterWithPanel="True"
Text="{Binding ContactusViewModel.Name, Mode=TwoWay}"
Margin="10,10"/>
<TextBox x:Name="txtEmail" MinWidth="320"
PlaceholderText="Email"
RelativePanel.AlignHorizontalCenterWithPanel="True"
RelativePanel.Below="txtName"
Margin="10,10"
Text="{Binding ContactusViewModel.Email, Mode=TwoWay}"
InputScope="EmailSmtpAddress" />
<TextBox x:Name="txtPhone" MinWidth="320"
PlaceholderText="Phone Number"
RelativePanel.AlignHorizontalCenterWithPanel="True"
RelativePanel.Below="txtEmail" Margin="10,10"
InputScope="TelephoneNumber"
Text="{Binding ContactusViewModel.PhoneNumber, Mode=TwoWay}"/>
<ComboBox x:Name="lstOptions" MinWidth="320" MinHeight="35"
RelativePanel.AlignHorizontalCenterWithPanel="True"
RelativePanel.Below="txtPhone" Margin="10,10"
ItemsSource="{Binding Path=ContactusViewModel.AvailableOptions, Mode=TwoWay}"
PlaceholderText="How can we help you"
IsSynchronizedWithCurrentItem="False"
DisplayMemberPath="Title">
</ComboBox>
<TextBox x:Name="txtMessage" MinWidth="320" MinHeight="150"
RelativePanel.AlignHorizontalCenterWithPanel="True"
RelativePanel.Below="lstOptions"
InputScope="Text" Margin="10,10"
Text="{Binding ContactusViewModel.Message, Mode=TwoWay}"/>
</RelativePanel>
</Grid>
View Model
public class ContactusViewModel : Template10.Mvvm.ViewModelBase
{
public string Name { get; set; }
public string Email { get; set; }
public int PhoneNumber { get; set; }
private ObservableCollection<Options> _options = new ObservableCollection<Models.Options>();
public ObservableCollection<Options> Options
{
get
{
if (_options == null || _options.Count <= 0)
{
_options.Add(new Options() { Id = 1, Title = "Development" });
_options.Add(new Options() { Id = 2, Title = "Training" });
_options.Add(new Options() { Id = 3, Title = "Consulting" });
}
return _options;
}
set
{
_options = value;
}
}
public string Message { get; set; }
// constructor
public ContactusViewModel()
{
_options.Add(new Options() { Id = 1, Title = "Development" });
_options.Add(new Options() { Id = 2, Title = "Training" });
_options.Add(new Options() { Id = 3, Title = "Consulting" });
// Options = _options;
}
public override void OnNavigatedTo(object parameter, NavigationMode mode, IDictionary<string, object> state)
{
base.OnNavigatedTo(parameter, mode, state);
}
}
I am using Options as observable collection to bind to Combo Box.
There is no AvailableOptions property on your view model, there is only Options.
You also need to have another property in the view model for the selected option, and bind it to the combo box's SelectedItem property.
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);
}
}
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.
I have a StackPanel in my grid and it displays a dynamically generated list of buttons, I'm trying to figure out how to get them displayed ascending alphabeticaly.
XAML Code
<StackPanel Grid.Column="0" Name="AreaStackPanel" Orientation="Vertical" Background="Khaki">
</StackPanel>
<StackPanel Grid.Column="1" Orientation="Horizontal" Background="Beige">
<GroupBox Name="StatusGroupBox" Header="Work Items" Width="234">
<StackPanel Name="StatusStackPanel"></StackPanel>
</GroupBox>
</StackPanel>
C# Code
private void LoadExistingAreas()
{
List<string> collections = Reporter.GetCollections();
string unique = "";
foreach (string collection in collections)
{
string areaName = Path.GetFileName(collection);
if (unique.Contains(areaName)) continue;
unique += areaName;
Button areaButton = new Button();
areaButton.Click += areaButton_Click;
areaButton.Margin = new Thickness(2);
areaButton.Content = areaName;
AreaStackPanel.Children.Add(areaButton);
Area
}
}
I would recommend using MVVM to accomplish this task. I am posting an example of what would work in a fairly clean fashion.
Your XAML should look as follows:
<Window x:Class="ItemTemplateDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ItemTemplateDemo"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<ItemsControl ItemsSource="{Binding ButtonDescriptions}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel></StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Margin="2" Content="{Binding Name}" Command="{Binding OnClickCommand}"></Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
The main view model. You should sort and filter your data in here as well
public class MainViewModel
{
public ObservableCollection<ButtonDescription> ButtonDescriptions { get; private set; }
public MainViewModel()
{
ButtonDescriptions = new ObservableCollection<ButtonDescription>();
for (int i = 0; i < 10; i++)
{
var bd = new ButtonDescription() { Name = "Button " + i };
ButtonDescriptions.Add(bd);
}
}
}
The button description holds the attributes for the button
public class ButtonDescription
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
private ICommand onClickCommand;
public ICommand OnClickCommand
{
get { return onClickCommand; }
set { onClickCommand = value; }
}
public ButtonDescription()
{
}
}
I would also recommend reading the following if you are not familiar with MVVM MVVM intro
I don't know according to MVVM show data on control.
I have a collection of cars.
I want group their by type (eg. Sedan, Combi, Hatchback) and depends of number of types print grids.
So :
5 cars:
2 x sedan, 2 x Combi, 1 x sportcar.
So I want to print 3 grids.
How do it to be ok with MVVM.
Below is some sample code. If your lists of cars can change you should use ObservableCollections instead or implement INotifyPropertyChanged on your viewmodel.
XAML:
<Window x:Class="TestApp.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<Grid>
<ListBox ItemsSource="{Binding Path=CarTypes}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=Key}" />
<ListBox ItemsSource="{Binding Path=Value}" DisplayMemberPath="Name" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
</Window>
Code behind:
using System.Collections.Generic;
using System.Windows;
namespace TestApp
{
public partial class Window2 : Window
{
public Window2()
{
InitializeComponent();
DataContext = new CarsVM();
}
}
public class CarsVM
{
public CarsVM()
{
CarTypes = new Dictionary<string, List<Car>>();
// You want to populate CarTypes from some model.
CarTypes["sedan"] = new List<Car>() {new Car("Honda Accord"), new Car("Toyota Camry")};
CarTypes["musclecar"] = new List<Car>() { new Car("Chevy Camaro"), new Car("Dodge Challenger") };
CarTypes["suv"] = new List<Car>() { new Car("Chevy Tahoe") };
}
public Dictionary<string, List<Car>> CarTypes { get; private set; }
}
public class Car
{
public Car(string name)
{
Name = name;
}
public string Name { get; set; }
}
}