XAML
<df:DataForm x:Name="MobCrud"
AutoEdit="True"
AutoCommit="True"
AutoGenerateFields="False"
VerticalAlignment="Top"
CommandButtonsVisibility="All"
Header="Mob Details"
CanUserAddItems="True"
CanUserDeleteItems="True"
CurrentItem="{StaticResource newMob}"
>
<df:DataForm.Fields>
<df:DataFormTextField Binding="{Binding Name}" FieldLabelContent="Name" />
<df:DataFormTextField Binding="{Binding Title}" FieldLabelContent="Title"/>
<df:DataFormComboBoxField x:Name="AuraList" Binding="{Binding Aura}" FieldLabelContent="Aura"/>
</df:DataForm.Fields>
Code:
public enum Auras
{
Holy,
Fire,
Frost,
}
public class MobDetail : IEditableObject
{
public string Name { get; set; }
public string Title { get; set; }
public Auras Aura { get; set; }
public override string ToString() { return Name; }
public void BeginEdit(){}
public void EndEdit(){}
public void CancelEdit(){}
}
The DataForm ItemsSource is bound to an ObservableCollection()
What do I need to do to populate and initialize the dropdown?
Answer is to use a converter:
<df:DataFormComboBoxField
x:Name="AuraList"
Binding="{Binding Aura, Mode=TwoWay,
Converter={StaticResource enumSelectedValueConverter}}"
FieldLabelContent="Aura"/>
and set the ItemsSource on the form Loaded event
(MobCrud.Fields[2] as DataFormComboBoxField).ItemsSource =
Enums.GetStringArray(typeof(Auras));
See here for the full story:
Creating-Rich-Data-Forms-in-Silverlight-3-Customization
Related
I have a datagrid which is bound to my ObservableCollection.
Each column is bound to property of the class wellenelement.
Now i would like to convert the column "Art" to a combobox column, where the user can choose from 3 different options.
How can i create these 3 Combobox items and add it to the datagrid?
<DataGrid AutoGenerateColumns="True" Name="dataGrid1" ItemsSource="{Binding}" >
</DataGrid>
```xaml
```c#
public partial class MainWindow : Window
{
public ObservableCollection<Wellenelement> Welle1;
public MainWindow()
{
InitializeComponent();
Welle1 = new ObservableCollection<Wellenelement>();
dataGrid1.DataContext = Welle1;
}
}
```c#
```c#
public class Wellenelement
{
public string Art { get; set; }
public string UK { get; set; }
public string DA { get; set; }
public string DI { get; set; }
}
```c#
If it's the same for each item you can add a collection to the viewmodel and add it as a resource to your view. Then you can set the resource as the itemssource for the DataGridComboBoxColumn.
ViewModel:
public class MainViewModel
{
public MainViewModel()
{
Wellenelements = new ObservableCollection<Wellenelement>()
{
new Wellenelement()
{
UK = "uk1",
DA = "da1",
DI = "di1"
},
new Wellenelement()
{
UK = "uk2",
DA = "da2",
DI = "di2"
},
};
ArtTypes = new List<string>()
{
"new art","old art", "good art","bad art"
};
}
public ObservableCollection<Wellenelement> Wellenelements { get; set; }
public List<string> ArtTypes { get; set; }
}
View:
<Window ...
xmlns:viewmodels="clr-namespace:WpfApp.Viewmodels"
...>
<Window.DataContext>
<viewmodels:MainViewModel/>
</Window.DataContext>
<Window.Resources>
<CollectionViewSource x:Key="myCollection" Source="{Binding ArtTypes}"/>
</Window.Resources>
<Grid>
<DataGrid AutoGenerateColumns="False"
CanUserAddRows="False"
ItemsSource="{Binding Wellenelements}" >
<DataGrid.Columns>
<DataGridComboBoxColumn ItemsSource="{Binding Source={StaticResource myCollection}}"
SelectedItemBinding="{Binding Art, UpdateSourceTrigger=PropertyChanged}"
Header="Art Combo column"/>
<DataGridTextColumn Binding="{Binding Art}"
IsReadOnly="True"
Header="Selected Art Type"/>
<DataGridTextColumn Binding="{Binding UK}"
Header="UK"/>
<DataGridTextColumn Binding="{Binding DA}"
Header="DA"/>
<DataGridTextColumn Binding="{Binding DI}"
Header="DI"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Wellenelement class:
public class Wellenelement : ObservableObject
{
public string UK { get; set; }
public string DA { get; set; }
public string DI { get; set; }
private string _art;
public string Art
{
get { return _art; }
set
{
_art = value;
OnPropertyChanged(nameof(Art));
}
}
}
I'm quite new to WPF and XAML and have been learning as I go.
I am writing a program in which the user needs to be able to enter data into a table, with pre-defined columns, which is then later sent to a database. I am, however, unable to make a DataGrid that allowes for this behaviour -
I'm using a - XAML is as follows:
<Grid MinHeight="100" MinWidth="600">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="20"/>
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Column="1"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.CanContentScroll="True">
<DataGrid SelectionMode="Single"
SelectionUnit="Cell"
CanUserAddRows="True"
CanUserDeleteRows="True"
IsReadOnly="False"
AutoGenerateColumns="False"
ItemsSource="{Binding SimpleCollection}">
<DataGrid.Columns>
<DataGridTextColumn Header="Rownummer" Binding="{Binding RowNumber}"/>
<DataGridTextColumn Header="Navn på felt" Binding="{Binding FieldName}"/>
<DataGridTextColumn Header="Forretningsmæssig nøgle" Binding="{Binding BusinessKey}"/>
<DataGridTextColumn Header="Er der datoopl." Binding="{Binding ContainsTimestamps}"/>
<DataGridTextColumn Header="Er der koder, der oversættes via BAS" Binding="{Binding ContainsBASTranslatedCodes}"/>
<DataGridTextColumn Header="Metadata" Binding="{Binding Metadata}"/>
<DataGridTextColumn Header="Evt. bemærkninger" Binding="{Binding AdditionalComments}"/>
</DataGrid.Columns>
</DataGrid>
</ScrollViewer>
</Grid>
<Grid>
The class used in the binding:
public class UserTable
{
public int RowNumber { get; set; }
public string FieldName { get; set; }
public string BusinessKey { get; set; }
public string ContainsTimestamps { get; set; }
public string ContainsBASTranslatedCodes { get; set; }
public string Metadata { get; set; }
public string AdditionalComments { get; set; }
public UserTable()
{
this.RowNumber = RowNumber;
this.FieldName = FieldName;
this.BusinessKey = BusinessKey;
this.ContainsTimestamps = ContainsTimestamps;
this.ContainsBASTranslatedCodes = ContainsBASTranslatedCodes;
this.Metadata = Metadata;
this.AdditionalComments = AdditionalComments;
}
public UserTable(int number, string name, string key, string timestamps, string translated, string meta, string additional)
{
RowNumber = number;
FieldName = name;
BusinessKey = key;
ContainsTimestamps = timestamps;
ContainsBASTranslatedCodes = translated;
Metadata = meta;
AdditionalComments = additional;
}
}
The creation of the Collection:
private ObservableCollection<UserTable> _simpleCollection;
public ObservableCollection<UserTable> SimpleCollection
{
get { return _simpleCollection ?? (_simpleCollection = new ObservableCollection<UserTable>()); }
set { _simpleCollection = value; }
}
(I am unsure where to place this - Have had it in the above mentioned class, and now in the .cs file that corresponds with the XAML)
Above code results in the following UI:
As you can probably see, the user is unable to enter anything into the table - There are no empty rows for data-insertion.
Does anyone have any idea about how to fix this?
To be able to add new rows to DataGrid you can create a ViewModel class and encapsulate your ObservableCollection<T> data collection in it, like i am going to show you.
for purpose of simplifying things let's say we have User model like this:
public class User
{
public int Id { get; set; }
public string FullName { get; set; }
public string Email { get; set; }
}
And let's create a simple view model class:
public class UsersViewModel
{
public UsersViewModel()
{
_users = new ObservableCollection<User>();
}
private ObservableCollection<User> _users;
public ObservableCollection<User> User => _users; // C# 6 feature.
}
Now we need to create an instance of UsersViewModel class and assign it as data context to any element that is parent to your DataGrid in our case let's assign it to the MainWindow Grid DataContext, we can do it in XAML or in code behind:
<Window xmlns:vm="clr-namespace:Namespace.In.Which.UsersViewModel.Lives">
<Window.Resources>
<vm:UsersViewModel x:Key="viewModel"></local:UsersViewModel>
</Window.Resources>
<Grid x:Name="mainGrid" DataContext="{StaticResource viewModel}"></Grid>
...
</Window>
Or you can do the same in code behind like this:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
mainGrid.DataContext = new UsersViewModel();
}
}
After that you need to bind the Users property of UserViewModel to DataGrid.ItemsSource property:
<DataGrid ...
ItemsSource="{Binding Users}"></DataGrid>
Now if you if you run your Application you will end up with an editable DataGrid.
You can set AutoGenerateColumns to true and the DataGrid will auto generate the correct columns type for your model.
I tested this steps on my machine and they work. Hopefully it helps you to solve your issue.
For the user to be able to add items in the DataGrid, your UserTable class must have a default parameterless constructor defined.
So you will either have to define one or remove the other constructor that accepts parameters:
public class UserTable
{
public int RowNumber { get; set; }
public string FieldName { get; set; }
public string BusinessKey { get; set; }
public string ContainsTimestamps { get; set; }
public string ContainsBASTranslatedCodes { get; set; }
public string Metadata { get; set; }
public string AdditionalComments { get; set; }
public UserTable() { } //<--
public UserTable(int number, string name, string key, string timestamps, string translated, string meta, string additional)
{
RowNumber = number;
FieldName = name;
BusinessKey = key;
ContainsTimestamps = timestamps;
ContainsBASTranslatedCodes = translated;
Metadata = meta;
AdditionalComments = additional;
}
}
I just can't make the following situation work:
I have a class, with the following implementation:
public class SelectionItem<T> : ViewModelBase where T : Entity
{
private bool? _isSelected;
public bool? IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
RaisePropertyChanged("IsSelected");
}
}
public T Item { get; set; }
}
And I have the following property on my ViewModel:
private IEnumerable<SelectionItem<DB_Aux_Pessoas>> _vendedores;
public IEnumerable<SelectionItem<DB_Aux_Pessoas>> Vendedores
{
get
{
return _vendedores;
}
set
{
_vendedores = value;
RaisePropertyChanged("Vendedores");
}
}
Then, in my View, I have the ComboBox:
<ComboBox Margin="3,0,0,0"
Height="23"
Width="200"
ItemsSource="{Binding Vendedores, Mode=TwoWay}"
Grid.Column="1"
Grid.Row="1"
HorizontalAlignment="Left">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}" />
<TextBlock Text="{Binding Item.NomeRazaoSocial}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
But when I change the CheckBox on the ComboBoxItem, it does not reflect on the property.
The code for DB_Aux_Pessoas is below:
[MetadataTypeAttribute(typeof(DB_Aux_Pessoas.DB_Aux_PessoasMetadata))]
public partial class DB_Aux_Pessoas
{
// This class allows you to attach custom attributes to properties
// of the DB_Aux_Pessoas class.
//
// For example, the following marks the Xyz property as a
// required property and specifies the format for valid values:
// [Required]
// [RegularExpression("[A-Z][A-Za-z0-9]*")]
// [StringLength(32)]
// public string Xyz { get; set; }
internal sealed class DB_Aux_PessoasMetadata
{
// Metadata classes are not meant to be instantiated.
private DB_Aux_PessoasMetadata()
{
}
public Nullable<short> Cliente { get; set; }
public string Id_Numero { get; set; }
public string NomeRazaoSocial { get; set; }
public Nullable<short> Supervisor { get; set; }
public Nullable<short> Vendedor { get; set; }
}
}
What I am doing wrong here?
Tks in advance.
I can't check this right now, but I'm fairly sure the dependency property IsChecked will only accept a binding to a bool and NOT a Nullable<bool>. You might have to do the conversion in your viewmodel and decide which should be the appropriate default for a null value.
This works fine for me. Add an extra property to give you some visual feedback like this:
public class SelectionItem<T> : ViewModelBase
{
private bool? _isSelected;
public bool? IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
RaisePropertyChanged("IsSelected");
RaisePropertyChanged("Feedback");
}
}
public string Feedback
{
get
{
if (!this.IsSelected.HasValue)
{
return "null";
}
if (this.IsSelected.Value)
{
return "yes";
}
return "no";
}
}
public T Item { get; set; }
}
And in your xaml, add an extra TextBlock to show the result:
<ComboBox Grid.Column="1"
Grid.Row="1"
Height="23"
Margin="3,0,0,0"
Width="200"
HorizontalAlignment="Left"
ItemsSource="{Binding Vendedores, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}" />
<TextBlock Text="{Binding Item.NomeRazaoSocial}" />
<TextBlock Text="{Binding Feedback}" Margin="5,0,0,0" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
If you see "Yes", "No", "Null" changing as you click the checkboxes, then it's working. Your code worked for me.
public static readonly DependencyProperty IsCheckedProperty = DependencyProperty.RegisterAttached("IsChecked", typeof(bool), typeof(...),
new PropertyMetadata(false, delegate(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
...
}
IsChecked is bool. You can possibly track BindingExpression Error in Output window.
I cannot get DataGrid binding to work in the example bellow.
Any clues on what's going on ?
namespace WPFTestApplication
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public class Person
{
public int age { get; set; }
public String Name { get; set; }
public Person(int age, String Name)
{
this.age = age;
this.Name = Name;
}
}
public class MegaObject
{
public IList<Person> persons { get; set; }
public MegaObject()
{
persons = new List<Person>();
persons.Add(new Person(11, "A"));
persons.Add(new Person(12, "B"));
persons.Add(new Person(13, "C"));
}
}
public Window1()
{
InitializeComponent();
MegaObject myobject= new MegaObject();
DataContext = myobject;
}
}
}
<Grid>
<my:DataGrid
Name="dataGrid"
AutoGenerateColumns="False"
ItemsSource="{Binding Source=persons}"
>
<my:DataGrid.Columns>
<my:DataGridTextColumn Binding="{Binding Path=age, Mode=TwoWay}" >
</my:DataGridTextColumn>
<my:DataGridTextColumn Binding="{Binding Path=Name, Mode=TwoWay}" >
</my:DataGridTextColumn>
</my:DataGrid.Columns>
</my:DataGrid>
</Grid>
Regards,
MadSeb
The ItemsSource binding needs to have Path set, not Source, to persons. Simply putting it as {Binding persons} would do the trick (Path is the default property in markup) or explicitly {Binding Path=persons}. The DataContext is always inherited.
I have a list of names that I'd like to have bound to a datagrid for editing/sorting/etc. But, I don't like how the DataGrid is displayed at all. Columns are placed in Alphabetical order when I really want a custom order (and I wish I could hide the ID column, or make that column not editable). I'm not sure how to start doing any of this...
NOTE: I removed a lot of "common" code (ex: INotifyPropertyChanged code...)
//PersonModel.cs
public class PersonModel
{
public Int32 ID { get; set; }
public String FirstName { get; set; }
public String LastName { get; set; }
}
//PersonViewModel.cs
public class PersonViewModel
{
public PersonViewModel()
{
Init();
}
public PersonViewModel(ObservableCollection<PersonModel> persons)
{
Init(person);
}
private void Init(ObservableCollection<PersonModel> persons = null)
{
Persons = person ?? new ObservableCollection<PersonModel>();
}
public ObservableCollection<PersonModel> Persons { get; set; }
}
//PersonView.xaml
<UserControl ...
...
<DataGrid ItemsSource="{Binding Persons}" />
...
</UserControl>
Unless you tell it otherwise, the DataGrid infers columns via reflection. If you want to take control, you can:
<DataGrid ItemsSource="{Binding Persons}">
<DataGrid.Columns>
<DataGridTextColumn Header="First Name" Binding="{Binding FirstName}"/>
<DataGridTextColumn Header="Last Name" Binding="{Binding LastName}"/>
</DataGrid.Columns>
</DataGrid>