Devexpress wpf CellEditor bind to class instance - c#

I have dxg:GridControl.
xaml:
<dxg:GridControl Name="DynamicGridControl"
ItemsSource="{Binding CommonEditCollection, Mode=TwoWay}"
SelectionMode="Cell"
AutoGenerateColumns="AddNew"
AutoGeneratedColumns="GridControl_AutoGeneratedColumns">
<dxmvvm:Interaction.Behaviors>
<lc:CellSelectionBehavior SelectedCells="{Binding SelectedCells, Mode=TwoWay}"/>
</dxmvvm:Interaction.Behaviors>
</dxg:GridControl>
ItemsSource binds to CommonEditCollection
viewmodel:
public ObservableCollection<Dictionary<int, DynamicTableModel>> CommonEditCollection { get; set; }
model:
public class DynamicTableModel
{
public double CellWidth { get; set; }
public string StrValue{ get; set; }
public bool IsBorerNull { get; set; }
public DynamicTableModel(string strVal, double cellWidth, bool isBorerNull = false)
{
StrValue = strVal;
CellWidth = cellWidth;
IsBorerNull = isBorerNull;
}
}
In xaml file I set Resources for cells style (I want to merge some cells):
<DataTemplate x:Key="CellDataTemplate">
<StackPanel>
<Border ...
</Border.Style>
</Border>
<dxg:CellEditor Content="{Binding Value.StrValue}"/>
</StackPanel>
</DataTemplate>
I bind CellEditor to property of DynamicTableModel class. But if I try edit text in any cell it throw NullReferenceException.
I cant change class DynamicTableModel to string because I need other properties. And I tried to use convertor Attribute, but it create new instance when I change text.
Help me please to change text in cells.
Project link: https://github.com/Kolgotin/DynamicGridControl

In result I just added this property:
<DataTemplate>
<dxe:TextEdit Name="PART_Editor" HorizontalContentAlignment="Stretch">
<dxe:TextEdit.EditTemplate>
<ControlTemplate>
<dxe:TextEdit x:Name="teNewValue"
HorizontalAlignment="Stretch"
EditValue="{Binding Value.StrValue}"/>
</ControlTemplate>
</dxe:TextEdit.EditTemplate>
</dxe:TextEdit>
</DataTemplate>
and handler works in "set" method

Related

Combobox Itemsource Binding does not work

I'm trying to fill my Combobox ItemsSource using Binding in Xaml with a collection of data ObservableCollection but I always get the Combobox ItemsSource null .
WPF UserControl Cs Code : ( Update the code )
public ObservableCollection<User> users { get; set; }
public partial class MainWindow : Window
{
InitializeComponent();
User user1 = new User("Mariah", 15);
User user2 = new User("John", 19 );
users = new ObservableCollections<User>();
users.Add(user1);
users.Add(user2);
this.DataContext = this;
Console.WriteLine(ComboBoxUsers.Items.Count); // always 0
Console.WriteLine(ComboBoxUsers.ItemsSource); // always null
}
WPF UserControl Xaml Code : ( Updated my code )
<ComboBox SelectedIndex = "0" x:Name="ComboBoxUsers" ItemsSource="{Binding users, UpdateSourceTrigger=PropertyChanged}" FontFamily="Arial" FontSize="11" Grid.Row="3" Height="30" Margin="10,5,5,5">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<Image IsEnabled="False" Source="{Binding Image}"/>
<TextBlock x:Name="textblock" IsEnabled="False" Text="{Binding Name} />
</StackPanel>
<DataTemplate.Resources>
<Style TargetType="ComboBoxItem">
<Setter Property="IsEnable" Value="{Binding IsEnable}"/>
</Style>
</DataTemplate.Resources>
</DataTemplate>
</ComboBox.ItemTemplate>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem" BasedOn="{StaticResource {x:Type ComboBoxItem}}">
<Setter
Property="Visibility"
Value="{Binding IsHidden}" />
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
Class User
public class User
{
public string Name { get; set; }
public int Age { get; set; }
public User(string name, int age)
{
this.Name = name;
this.Age = age;
}
}
What is the source of this problem?
Discarding unnecessary (which is not relevant to the question or has syntax errors), your example with a little formatting works fine.
XAML:
<ComboBox x:Name="ComboBoxUsers"
ItemsSource="{Binding Users}"
FontFamily="Arial"
FontSize="11"
Grid.Row="3"
Height="30"
Margin="10,5,5,5">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<TextBlock IsEnabled="False"
Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Code-behind:
public class User
{
public string Name { get; }
public int Age { get; }
public User(string name, int age)
{
Name = name;
Age = age;
}
}
public partial class MainWindow : Window
{
public ObservableCollection<User> Users { get; }
public MainWindow()
{
InitializeComponent();
// Initialize collection with some items
Users = new ObservableCollection<User>
{
new User("Mariah", 15),
new User("John", 19)
};
DataContext = this;
}
}
Result:
Remarks:
Your
Console.WriteLine(ComboBoxUsers.Items.Count); // always 0
Console.WriteLine(ComboBoxUsers.ItemsSource); // always null
because you use Binding. You needn't access ItemsSource or Items.Count through ComboBox - you should use binded collection Users (e.g. Users.Count) to manipulate or get ComboBox content.
EDIT.
About SelectedItem. You should define for yourself, you want use Bindings or work with ComboBox directly.
Binding push you to NOT use ComboBox.SelectedItem/Index/Value whatever. Even not access ComboBoxUsers to get some data. Binding is closely related to, for example, the MVVM Pattern. If you decided to use Bindings - forget about accessing directly to ComboBox or it data properties SelectedItem/Index/Value or similar.
If you use Bindings - you should create a property (e.g. SelectedUser) for SelectedItem (same as you create property Users for your ItemsSource ) and bind to it:
XAML (introducing binding for SelectedItem property and SelectionChanged handler):
<ComboBox x:Name="ComboBoxUsers"
ItemsSource="{Binding Users}"
SelectedItem="{Binding SelectedUser}"
SelectionChanged="OnUserSelectionChanged"
FontFamily="Arial"
FontSize="11"
Grid.Row="3"
Height="30"
Margin="10,5,5,5">
</ComboBox>
Code-behind (introducing property SelectedUser and OnUserSelectionChanged handler):
public partial class MainWindow : Window
{
public ObservableCollection<User> Users { get; }
// Here would be stored your Binded selected item
public User SelectedUser { get; set; }
// And here is your handler when selection changed
private void OnUserSelectionChanged(object sender, SelectionChangedEventArgs e)
{
// SelectedUser property stores selected in ComboBox item,
// so you can use it directly
_ = MessageBox.Show(SelectedUser.Name);
// Even if you wish to get directly - it is possible
// (thanks to #Clemens):
var selectedUser = (sender as ComboBox).SelectedItem as User;
var selectediIndex = (sender as ComboBox).SelectedIndex;
}
public MainWindow()
{
InitializeComponent();
Users = new ObservableCollection<User>
{
new User("Mariah", 15),
new User("John", 19)
};
DataContext = this;
}
}
Repeat same algorithm for each property you wish to Bind (e.g. SelectedIndex):
SelectedIndex="{Binding SelectedUserIndex}"
public int SelectedUserIndex { get; set; }
Result:
Decide for yourself, what you need. Nice modern Bindings or old boring (sender as ComboBox).SelectedItem.

Change the Background of an ELEMENT within a DataTemplate

I've looked online and found some topics related to my issue, although being new to XAML and WPF, i'm having trouble making what I want work.
I have a custom TimeLineControl StackPanel that contains 'Items' of type TimeLineFunctionControl, where the Items uses a DataTemplate to define how the 'Items' are displayed.
<!-- Static Resource = BgColor -->
<Color R="255" G="255" B="255" A="180" x:Key="BgColor" />
<!-- Static Resource = BgBrush -->
<SolidColorBrush Color="{DynamicResource BgColor}" x:Key="BgBrush" />
<!-- DataTemplate = TimeLineFunctionDataTemplate -->
<DataTemplate DataType="{x:Type tt:FunctionDataType}"
x:Key="TimeLineFunctionDataTemplate">
<Border x:Name="DataContainer"
BorderThickness="0.3"
BorderBrush="Black"
CornerRadius="2"
Margin="0,20,0,10"
Height="50"
Background="{DynamicResource BgBrush}">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Path=Name}" FontWeight="Bold"/>
<TextBlock Text="{Binding Path=StartTime.TotalMilliseconds, StringFormat={}{0} ms}" FontSize="8"/>
<TextBlock Text="{Binding Path=EndTime.TotalMilliseconds, StringFormat={}{0} ms}" FontSize="8"/>
</StackPanel>
</Border>
</DataTemplate>
public class FunctionDataType : ITimeLineDataItem
{
public TimeSpan? StartTime { get; set; }
public TimeSpan? EndTime { get; set; }
public Boolean TimelineViewExpanded { get; set; }
public String Name { get; set; }
}
I want to be able to change the Background color of the Border (DataContainer) dynamically from within the code.
I've tried the following;
1 - Doesn't work, I've since learnt that once a Template is applied, the Background property is no longer used.
titem.Background = (Brush)FindResource("BgBrushTriggered");
2 - Works, although I need to have defined two (2) DataTemplate in XAML, each with different Background colors, seems there must be a better way to do it.
titem.ContentTemplate = (DataTemplate)FindResource("TimeLineFunctionDataTriggeredTemplate");
3 - Works, although it changes ALL the items, since i'm changing the DynamicResource value.
this.Resources["BgBrush"] = new SolidColorBrush((Color)FindResource("BgColorTriggered"));
4 - Doesn't work, XAML reports "The member "Background" is not recognized or is not accessible";
Background="{TemplateBinding Background}"
Background="{Binding Background, RelativeSource={RelativeSource TemplatedParent}"
Q: What are my options?
Q: Is there a good online resource to correctly learn XAML and now to apply bindings, styles, templates etc...
The correct way to do this would be to bind the Background property of the Border to a property of the FunctionDataType objects and then set this property of the particular item you want to change.
You could either bind directly to a Brush property or define some other type of property and use a converter to convert this value into a Brush. The FunctionDataType must implement the INotifyPropertyChanged interface for this to work.
Please refer to the following sample code.
<DataTemplate DataType="{x:Type tt:FunctionDataType}"
x:Key="TimeLineFunctionDataTemplate">
<Border x:Name="DataContainer"
BorderThickness="0.3"
BorderBrush="Black"
CornerRadius="2"
Margin="0,20,0,10"
Height="50"
Background="{Binding BgBrush}">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Path=Name}" FontWeight="Bold"/>
<TextBlock Text="{Binding Path=StartTime.TotalMilliseconds, StringFormat={}{0} ms}" FontSize="8"/>
<TextBlock Text="{Binding Path=EndTime.TotalMilliseconds, StringFormat={}{0} ms}" FontSize="8"/>
</StackPanel>
</Border>
</DataTemplate>
public class FunctionDataType : ITimeLineDataItem, INotifyPropertyChanged
{
public TimeSpan? StartTime { get; set; }
public TimeSpan? EndTime { get; set; }
public Boolean TimelineViewExpanded { get; set; }
public String Name { get; set; }
private Brush _bgBrush;
public Brush BgBrush
{
get { return _bgBrush; }
set { _bgBrush = value; NotifyPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
var item = titem.DataContext as FunctionDataType;
item.BgBrush = System.Windows.Media.Brushes.Red;
you can use a property in your model ItemColorIndex and then bind the background via a converter to it Background="{Binding ItemColorIndex , Converter=XXX}

UWP Invoke method from DataTemplate Object

Self-taught programmer, would love any constructive criticism regarding my code.
I have a ListView that will have ListViewItems that I want to customize.
The ListViewItem I have made has two TextBlocks and a ToggleSwitch. When the ToggleSwitch is switched On/Off I want it to call a method from an instantiate object, or call a method from the same form, but somehow retrieve the object that initially loaded into the DataTemplate.
Here is the XAML so far:
<ListView x:Name="listViewAddedVideoFolders" Grid.Row="1" DoubleTapped="listViewAddedVideoFolders_DoubleTapped" SelectionChanged="listViewAddedVideoFolders_SelectionChanged" HorizontalContentAlignment="Stretch">
<ListView.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<TextBlock HorizontalAlignment="Stretch" VerticalAlignment="Center" Text="{Binding Directory}"/>
<Grid HorizontalAlignment="Right">
<StackPanel>
<TextBlock Text="Find Videos: "></TextBlock>
<ToggleSwitch Toggled="listViewVideoFolder_toggled" />
</StackPanel>
</Grid>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
Right now it is calling listViewVideoFolder_toggled
Before I was trying to use Toggled="{Binding StartCrawling()}"
Here is the AddVideoFolderModel object that I am binding the listviewitems to
namespace Movie_Management_Windows_10.Models
{
public class AddVideoFolderModel
{
public static ObservableCollection<AddVideoFolderModel> MyVideoFolderModels = new ObservableCollection<AddVideoFolderModel>();
public int VideosFound { get; set; }
public string Directory { get; set; }
public string DirectoryName { get; set; }
private bool isCrawling = false;
public bool HasBeenCrawled = false;
private void startCrawling()
{
AppShell.Current.NotifyUser("Crawling began", AppShell.NotifyType.StatusMessage);
}
//public override string ToString()
//{
// return Directory + " (" + VideosFound.ToString() + ")";
//}
}
}
What must I implement to accomplish this?
At first, you can add property to your model and bind to IsOn property in ToggleSwitch using TwoWay mode binding. Is this case, your model must implement INotifyPropertyChanged
private bool _isNeedCrawle;
public bool IsNeedCrawle
{
get
{
return _isNeedCrawle;
}
set
{
if (_isNeedCrawle != value)
{
_isNeedCrawle = value;
if (_isNeedCrawle)
{
startCrawling();
}
NotifyPropretyChanged("IsNeedCrawle");
}
}
}
At second, you can use XAML Behavior SDK. In this case, you must Add reference to library (look how to do it), and change method modifier from private to public
xmlns:i="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
<ToggleSwitch>
<i:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Toggled">
<core:CallMethodAction MethodName="StartCrawling" TargetObject="{Binding }"/>
</core:EventTriggerBehavior>
</i:Interaction.Behaviors>
</ToggleSwitch>

How to bind events and properties of controls in UniformGrid dynamically?

I am a newbie in templating Wpf controls. I use VS2013, WPF 4.5 and Caliburn Micro 2.0.2. In part of tasks I have I need to populate a grid with toggle buttons contained different images and its subtitle. I have solved it using UniformGrid. See my code below. They work but still don't have event and property binding since I don't know how I can bind the events and properties of toggle buttons to view model, since they are generated automatically and dynamically and the number of toggle buttons is uncertain (depends on the number of images in the image folder).
For example:
manually I could bind the Click event, IsChecked property and some other properties of toggle button 1 like following:
<ToggleButton x:Name="ToggleVehicle01" IsChecked={Binding SelectedVehicle01} Background="{Binding BackColorSelectedVehicle01}" ToolTip="{Binding VehicleName01}">
But now I can't do that anymore since the toggle buttons are generated automatically and their number is uncertain. Please help. Feel free to change my code below or give me examples code that works. Thank you in advance.
The View (MainView.xaml):
<UserControl x:Class="CMWpf02.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<Grid Width="1024"
Height="768"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ShowGridLines="True">
<ItemsControl Name="ImageList"
Background="#FFFFFFFF"
BorderBrush="#FFA90606"
ItemsSource="{Binding Path=VehicleImages}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Margin="0,0,0,0" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ToggleButton Width="180"
Margin="10,10,10,10"
FontSize="10"
Style="{StaticResource {x:Static ToolBar.ToggleButtonStyleKey}}">
<!-- x:Name="ToggleVehicle01" -->
<!-- Background="{Binding BackColorSelectedVehicle01}" -->
<!-- IsChecked="{Binding SelectedVehicle01}" -->
<!-- ToolTip="{Binding Vehicle01Name}"> -->
<StackPanel Margin="0,5,0,5"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Image Width="140"
RenderOptions.BitmapScalingMode="Fant"
Source="{Binding Path=Image}" />
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
FontWeight="Bold"
Text="{Binding Path=Name}" />
</StackPanel>
</ToggleButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
The ViewModel (MainViewModel.cs):
using Caliburn.Micro;
using System;
using System.Collections.ObjectModel;
using System.IO;
namespace CMWpf02.ViewModels
{
public class MainViewModel : Screen, IHaveDisplayName
{
private String _path2Images = #"D:\tmp\Images";
public string DisplayName { get; set; }
public ObservableCollection<VehicleImage> VehicleImages { get; set; }
public MainViewModel()
{
DisplayName = "Main Window";
var vehicles = new ObservableCollection<String>();
vehicles = GetAllFilesFromFolder(_path2Images);
VehicleImages = new ObservableCollection<VehicleImage>();
foreach (var i in vehicles)
VehicleImages.Add(new VehicleImage(i));
}
public ObservableCollection<String> GetAllFilesFromFolder(String fullPathFolder)
{
string[] fileArray = Directory.GetFiles(fullPathFolder);
return new ObservableCollection<String>(fileArray);
}
}
public class VehicleImage
{
public String Image { get; private set; }
public String Name { get; private set; }
public VehicleImage(String image)
{
Image = image;
Name = Path.GetFileName(image);
}
}
//public void ToggleVehicle01()
//{
// var selectText = (SelectedVehicle01) ? " selected" : " unselected";
// MessageBox.Show(Vehicle01Name + selectText);
// BackColorSelectedVehicle01 = (SelectedVehicle01) ? _backColorSelectedVehicle : _defaultBackColorVehicle;
//}
//public Boolean SelectedVehicle02
//{
// get { return _selectedVehicle02; }
// set
// {
// _selectedVehicle02 = value;
// NotifyOfPropertyChange(() => SelectedVehicle02);
// }
//}
//public Brush BackColorSelectedVehicle02
//{
// get { return _backColorSelectedVehicle02; }
// set
// {
// _backColorSelectedVehicle02 = value;
// NotifyOfPropertyChange(() => BackColorSelectedVehicle02);
// }
//public String Vehicle01Name { get; private set; }
}
EDIT: Now I can bind the properties of generated ToggleButton with view model. I make the VehicleImage class to a view model (see modified code below). But I still have problem to bind Click-event of generated ToggleButton to view model.
The modified class to view model
public class VehicleImage : PropertyChangedBase
{
public String Image { get; private set; }
public String Name { get; private set; }
private Boolean _selectedVehicle;
public Boolean SelectedVehicle
{
get { return _selectedVehicle; }
set
{
_selectedVehicle = value;
BackColorSelectedVehicle = _selectedVehicle ? new SolidColorBrush(Color.FromArgb(255, 242, 103, 33)) : new SolidColorBrush(Colors.White);
}
}
private Brush _backColorSelectedVehicle;
public Brush BackColorSelectedVehicle
{
get { return _backColorSelectedVehicle; }
set
{
_backColorSelectedVehicle = value;
NotifyOfPropertyChange(() => BackColorSelectedVehicle);
}
}
// ToggleButton's Click-Event Handler, but it doesn't get event trigger from View.
// Therefore I set the BackColorSelectedVehicle fin setter of SelectedVehicle property.
public void ToggleSelection()
{
//BackColorSelectedVehicle = SelectedVehicle ? new SolidColorBrush(Color.FromArgb(255, 242, 103, 33)) : new SolidColorBrush(Colors.White);
}
public VehicleImage(String image)
{
Image = image;
Name = Path.GetFileName(image);
}
}
The modified view
<ToggleButton Width="180"
Margin="10,10,10,10"
Background="{Binding Path=BackColorSelectedVehicle}"
FontSize="10"
IsChecked="{Binding Path=SelectedVehicle}"
Style="{StaticResource {x:Static ToolBar.ToggleButtonStyleKey}}"
ToolTip="{Binding Path=Name}">
<!-- x:Name="ToggleSelection" -->
<StackPanel Margin="0,5,0,5"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Image Width="140"
RenderOptions.BitmapScalingMode="Fant"
Source="{Binding Path=Image}" />
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding Path=Name}" />
</StackPanel>
</ToggleButton>

Databinding a nested List property in WPF

I am using the following XAML code to display a list of checked list boxes.
<ListBox x:Name="lbxProjects" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<ListBox x:Name="lbxUnits" ItemsSource="{Binding Units}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<CheckBox Content="{Binding unit.Name}" IsChecked="{Binding isSelected}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The data model is as follows
public class ProjectsListBox
{
public Project project { get; set; }
public List<UnitsCheckBox> Units = new List<UnitsCheckBox>();
public ProjectsListBox(Project project)
{
this.project = project;
foreach(var d in project.Documents)
{
Units.Add(new UnitsCheckBox(d));
}
}
}
public class UnitsCheckBox : INotifyPropertyChanged
{
public Document unit { get; set; }
private bool isselected = true;
public bool isSelected
{
get { return isselected; }
set
{
isselected = value;
NotifyPropertyChanged("isSelected");
}
}
public UnitsCheckBox(Document d)
{
unit = d;
}
}
I am assigning the data source for the parent listbox like
lbxProjects.DataContext = projectsList;
The code creates the child list boxes but not the checkboxes inside the child list boxes. What am i missing?
How should WPF resolve unit.Name?
If the type UnitsCheckBox contains a Name property, then the CheckBox's Content should be bound to Name:
Content="{Binding Name}"
You should always specify the type of your DataTemplate:
<DataTemplate DataType="{x:Type local:UnitsCheckBox}" ...>
Those are the probable problems but I can't be sure unless you give us the UnitsCheckBox code.

Categories