How can I get the text of a RadAutoCompleteBox using RadControls Q1 2013 in C#?
autoCompleteBox.SelectedItem returns "ServerCrafterTelerikWPF.Command".
Edit 1:
Here's my XAML:
<telerik:RadAutoCompleteBox x:Name="txtboxCommand" ItemsSource="{Binding Commands, Source={StaticResource ViewModel}}"
DisplayMemberPath="ACommand" AutoCompleteMode="Append" HorizontalAlignment="Left"
telerik:StyleManager.Theme="Modern" Margin="280,405,0,0"
VerticalAlignment="Top" Width="330" Height="30" KeyDown="txtboxCommand_KeyDown"/>
And I don't have any C# code. I just want, when a button is pressed, to get the text that is in the RadAutoCompleteBox.
Edit 2:
And here's my collection:
public class Command
{
public string ACommand { get; set; }
}
/// <summary>
/// A view model for MainWindow.xaml
/// </summary>
public class ViewModel
{
public ObservableCollection<Command> Commands { get; set; }
public ViewModel()
{
Commands = new ObservableCollection<Command>()
{
new Command() {ACommand = "stop "},
// Other commands...
// ...
// ...
};
}
}
You should take it from the SelectedItem property. Cast it to your class and then get it from MyClass.ACommand
And I suggest binding SelectedItem with Mode=TwoWay in your ViewModel can help a lot.
Just add a Member to ViewModel which is implementing Command like:
private Command _SelectedItem;
public Command SelectedItem
{
//get set with INotifyPropertyChanged
}
Then from the xaml: Bind RadAutoCompleteBox's SelectedItem Property like:
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
I have reproduced the problem.
Yes. I had the same problem. And I found the problem and the answer too.
I got the problem because of using of type string for the selected item in my view model.
private string selectedCommand;
public string SelectedCommand
{
get
{
return selectedCommand;
}
set
{
selectedCommand = value;
NotifyPropertyChanged("SelectedCommand");
}
}
Use the type as Command class and your problem will be solved.
private Command selectedCommand;
public Command SelectedCommand
{
get
{
return selectedCommand;
}
set
{
selectedCommand = value;
NotifyPropertyChanged("SelectedCommand");
}
}
Bind the SelectedItem property of the RadAutoCompleteBox in the XAML
<telerik:RadAutoCompleteBox
x:Name="txtboxCommand"
ItemsSource="{Binding Commands, Source={StaticResource ViewModel}}"
DisplayMemberPath="ACommand"
AutoCompleteMode="Append"
HorizontalAlignment="Left"
telerik:StyleManager.Theme="Modern"
Margin="280,405,0,0"
VerticalAlignment="Top"
Width="330"
Height="30"
KeyDown="txtboxCommand_KeyDown"
SelectedItem="{Binding SelectedCommand, Mode=TwoWay}"/>
If you wanna get the selected item by the code-behind, convert the selected item to the Command class type.
var selectedItem = autoCompleteBox.SelectedItem as Command;
And actually there can be multiple selected items. In that case you have to define a collection of Command objects.
private ObservableCollection<Command> selectedCommands;
public ObservableCollection<Command> SelectedCommands
{
get
{
return selectedCommands;
}
set
{
selectedCommands = value;
NotifyPropertyChanged("SelectedCommands");
}
}
And bind it to the SelectedItems property (plural of SelectedItem) of the RadAutoCompleteBox control.
SelectedItems="{Binding SelectedCommands, Mode=TwoWay}"
And make sure you have initiated the SelectedItems.
this.SelectedCommands = new ObservableCollection<Command>();
The SearchText property of the RadAutoCompleteBox should provide you the value.
According to the documentation it gets or sets the string that is into the TextBox part of the RadAutoCompleteBox. The SearchText value is used to filter the RadAutoCompleteBox' ItemsSource.
If you want to get the "Text" of the selected item of the AutocompleteBox, then you need to cast it to the specified type. In your case it is of type ServerCrafterTelerikWPF.Command.
var selectedItem = autoCompleteBox.SelectedItem;
if (selectedItem is ServerCrafterTelerikWPF.Command) {
var selectedCommand = selectedItem as ServerCrafterTelerikWPF.Command;
string textOfAutoCompleteBox = selectedCommand.ACommand;
}
Related
I have ComboBox with "ProjectList":
MainWindow.xaml
<ComboBox ItemsSource="{Binding Path=ProjectList}" IsSynchronizedWithCurrentItem="True" />
Elements are adding inside below method and working ok:
MainViewModel.cs
[AddINotifyPropertyChangedInterface]
public class MainViewModel{
public ObservableCollection<string> ProjectList { get; internal set; }
= new ObservableCollection<string>();
public async Task GetProjects(){
...
foreach (AItem item in....)
{
ProjectList.Add(item.name)
}
}
}
Note - I have installed "PropertyChanged.Fody".
Now I have added second ComboBox "TaskList" to xaml:
<ComboBox ItemsSource="{Binding Path=TaskList}" IsSynchronizedWithCurrentItem="True" />
List here should be created based on selected item into "ProjectList". Method is mostly the same, only has a parameter:
public ObservableCollection<string> TaskList { get; internal set; }
= new ObservableCollection<string>();
public async Task GetTask(string projectId = ""){
...
foreach (AItem item in....)
{
TaskList.Add(item.name2)
}
}
Now I want to addopt this to my MVVM.
Problem is: when and how to run GetTask()? Instead of "internal set" for ObservableCollection TaskList should be implemented "onpropertychanged"?
when you select Project the next cb should load (run) taskList
Then you should either bind the SelectedItem property of the ComboBox to a string source property and call the GetTask method in the setter of this one, e.g.:
private string _selectedProject;
public string SelectedProject
{
get { return _selectedProject; }
set
{
_selectedProject = value;
GetTask(_selectedProject);
}
}
...or invoke a command when the selection changes: Either from the setter:
set
{
_selectedProject = value;
YourCommand.Execute(null);
}
...or from the XAML markup using an interaction trigger.
Properties should not not really be kicking off asynchronous background operations in their setters. Please refer to my answer here for more information about this:
Is wrong to invoke an async method from property setter in WPF view model?
You need an event that's fired when the selected item of the ProjectList Combo box changes.
If you were using the code behind, you can just set the SelectionChanged property to point to a function. However it appears that you're using MVVM, so you need to add an interaction trigger to the combobox, a command for the trigger to call and a method for the command to call.
Add this namespace to the window:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Change your combobox to include the trigger and to expose the SelectedItem
<ComboBox ItemsSource="{Binding Path=ProjectList}"
IsSynchronizedWithCurrentItem="True"
SelectedItem="{Binding SelectedProject}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding ReloadTasksCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
Then, in your ViewModel, add the ReloadTasksCommand command. You need to define the command, and the method that it calls.
private ICommand reloadTasks;
public ICommand ReloadTasksCommand => this.reloadTasks?? (this.reloadTasks= new RelayCommand(this.ReloadTasks));
public string SelectedProject{ get; set; }
private void ReloadTasks()
{
GetTasks(this.SelectedProject);
}
I've also added a property for the SelectedProject, bound to the SelectedItem of the Project combobox.
RelayCommand is in the GalaSoft.MvvmLight.CommandWpf library. There are others, that's just what I use.
Change combobox like this:
<ComboBox ItemsSource="{Binding Path=ProjectList}"
IsSynchronizedWithCurrentItem="True"
SelectedItem="{Binding SelectedProject}" >
</ComboBox>
ViewModel:
private string _selectedObject;
public string SelectedObject
{
get { return _selectedObject; }
set
{
_selectedObject = value;
//OnPropertyChanged(); -- INotifyPropertyChanged no need if Fody
ReloadTasks();
}
}
private void ReloadTasks()
{
GetTasks(SelectedProject);
}
I found some topics "How to bind a combobox from enum list", but my problem is that I try to use MVVM arhitecture and let my View (xaml) clear as you see below:
part of View (xaml.cs):
public partial class GreenCertificatesStockForm : Erp.Core.Wpf.BaseWindow
{
private Models.GreenCertificatesGroupModel model;
public GreenCertificatesStockForm()
{
model = new Models.GreenCertificatesGroupModel();
this.DataContext = model;
InitializeComponent();
model.LoadForm(); // propose some dates for my form
model.RequestClose += () => { Close(); };
}
}
part of View (xaml) my RadComboBox:
<telerik:RadComboBox Name="certificatesTypeRadComboBox"
Margin="5 2 0 2" Width="150"
SelectedValue="{Binding CertificatesTypeEnum , Mode=TwoWay,
ValidatesOnDataErrors=True,
ValidatesOnExceptions=True,
NotifyOnValidationError=True}"
ItemSource="{Binding }"
SelectedItem="{Binding }"
telerik:StyleManager.Theme="Office_Blue" BorderBrush="#FF707070" Background="#FFDDDDDD"
>
</telerik:RadComboBox>
So, my Enum list is created in my ViewModel (class.cs) as:
public enum CertificatesTypeEnum {
Estimat = 1,
Calculat = 2,
Facturat = 3
}
I need to display in my RadComboBox all Values of the Enum and by selectedValue to save the specific Key of selection in DB (this with a parameter). How can I display the values from ViewModel into ComboBox (View)?
I know can do something like :
var items = Enum.GetValues(typeof(CertificatesTypeEnum)).Cast<CertificatesTypeEnum>().Select(i => new ComboboxItem()
{ Text = Enum.GetName(typeof(gender), i), Value = (int)i}).ToArray<ComboboxItem>();
//Add the items to your combobox (given that it's called comboBox1)
RadComboBoxName.Items.AddRange(items);
but this must be made in xaml.cs file (and I don't want this solution), because in ViewModel the combobox are not recognised and will be not found.
In short : display Values of Enum list from ViewModel class in xaml file.
Why don't you just call the Enum.GetValues method in the view model? This is MVVM:
public class GreenCertificatesGroupModel
{
public IEnumerable<CertificatesTypeEnum> EnumValues
{
get
{
var list = Enum.GetValues(typeof(CertificatesTypeEnum)).Cast<CertificatesTypeEnum>();
return list;
}
}
private CertificatesTypeEnum _selectedItem;
public CertificatesTypeEnum SelectedItem
{
get { return _selectedItem; }
set { _selectedItem = value; }
}
}
XAML:
<telerik:RadComboBox ItemsSource="{Binding EnumValues}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" />
Here's my issue...I have a list of servers, each with an ID and ServerName. I want to be able to select a server from the ComboBox and edit it in place, then have its ID available to update via SQL later. So let's say this is the data: (ID=1, Name="Server1"), (ID=2, Name="Server2"), (ID=3, Name="Server3"). If I select Server3 from the ComboBox, I'd like to edit it to be "Server4" then upload that with a SQL query (I know how to do this part). I'm utilizing MVVM, so all the values are properties of my ViewModel.
Currently, when the text field is modified in the ComboBox the SelectedServer immediately becomes null, presumably because it is no longer a value it recognizes. I could use some guidance on how to get this to do what I'm trying to do.
<ComboBox Grid.Column="1" x:Name="serverNameUpdateBox" HorizontalAlignment="Stretch" Height="23" VerticalAlignment="Center" IsEditable="True"
ItemsSource="{Binding Path=DataContext.SelectedProjectServers, ElementName=main}"
DisplayMemberPath="ServerName"
SelectedValue="{Binding SelectedServer}"
SelectedValuePath="ServerName"
Text="{Binding SelectedServer.ServerName, UpdateSourceTrigger=LostFocus}"
/>
And ViewModel relevant code:
namespace ViewModel
{
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
SelectedProjectServers = new List<Server>();
SelectedServer = new Server();
private Server _selectedServer;
public Server SelectedServer
{
get { return _selectedServer; }
set
{
if (value == null) { ModifiedServer = _selectedServer; }
_selectedServer = value;
RaisePropertyChanged("SelectedServer");
}
}
private List<Server> _selectedProjectServers;
public List<Server> SelectedProjectServers
{
get { return _selectedProjectServers; }
set
{
_selectedProjectServers = value;
RaisePropertyChanged();
}
}
}
}
}
And Model relevant code:
namespace Model
{
public class Server : INotifyPropertyChanged
{
private string _serverName;
public string ServerName
{
get { return _serverName; }
set
{
_serverName = value;
RaisePropertyChanged();
}
}
private int _serverID;
public int ServerID
{
get { return _serverID; }
set
{
_serverID = value;
RaisePropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged([CallerMemberName] string caller = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(caller));
}
}
}
Bind a property like "EditedServerName" to Combobox.Text.
When the "EditedServerName" is changed you can set the value to the "ServerName" of your SelectedServer.
<ComboBox Grid.Column="1" x:Name= "serverNameUpdateBox" HorizontalAlignment= "Stretch" Height= "23" VerticalAlignment= "Center" IsEditable= "True"
ItemsSource= "{Binding Path=DataContext.SelectedProjectServers, ElementName=main}"
DisplayMemberPath= "ServerName"
SelectedItem="{Binding SelectedServer}"
Text= "{Binding EditedServerName, UpdateSourceTrigger=LostFocus}"
/>
ComboBox is primarily used for selection. You could have used another control like datagrid or so for update functionality.
Well, if you want to do it the ComboBox way, I suggest that you place a couple of text boxes below your combo box and bind these text boxes content to the SelectedServer properties i.e.
<TextBox x:name="ServerName" Text ={Binding SelectedServer.ServerName} />
and so on.
So, when ever a server is selected, these text boxes will be filled with values from currently selected Server. And then, you can trigger some command using a button below these boxes which triggers the sql query and passes the required data using bound properties from text boxes.
I hope you got the idea.
For the purpose of code reuse, I am attempting to bind a ComboBox ItemsSource to an enumerable of enum values defined in a viewmodel. (I am aware of the strategies for binding directly to the enum, but in order to achieve code reuse I need to bind to an enumerable.) On viewmodel construction, I set the selected item to the first value of the enumerable. When the UI first launches, however, the combobox loads with validation error:
Value '' could not be converted.
This error does not occur when I use the same XAML to bind to an enumerable of classes. After I select an enum value, I get no more validation errors and the UI works as intended. How do I avoid this error and get the combobox to display the selected item on startup?
The code details... I have a service implementing IAcquire<T> which returns an enumerable of enum values:
public interface IAcquire<T>
{
IReactiveList<T> Items { get; }
}
My viewmodel inheritance looks something like this:
class GranularitySelectionViewModel : ChartFilterSelectionBase<DataGranularity>
{
public GranularitySelectionViewModel([NotNull] IAcquire<DataGranularity> service)
: base(service, "Granularity")
{}
}
class ChartFilterSelectionBase<T> : SelectionViewModelBase
{
private readonly IAcquire<T> _service;
internal ChartFilterSelectionBase([NotNull] IAcquire<T> service, string label)
:base(label)
{
foreach (var value in service.Items)
{
Items.Add(value);
}
SelectedItem = Items.FirstOrDefault();
}
private readonly IReactiveList<T> _items = new ReactiveList<T>();
public new IReactiveList<T> Items
{
get { return _items; }
}
private T _selectedItem;
public new T SelectedItem
{
get { return _selectedItem; }
set { SetProperty(ref _selectedItem, value); }
}
}
public class SelectionBaseViewModel
{
protected SelectionBaseViewModel([NotNull] string label )
{
if (label == null) throw new ArgumentNullException("label");
_label = label;
}
private readonly string _label;
public string Label
{
get { return _label; }
}
//Placeholder to be overridden in derived class.
public object SelectedItem { get; set; }
//Placeholder to be overridden in derived class.
public IReactiveList<object> Items { get; private set; }
}
The XAML is as follows:
<DataTemplate DataType="{x:Type viewModels:SelectionBaseViewModel}">
<StackPanel Orientation="Vertical">
<Label Content="{Binding Label}" ContentStringFormat="{}{0}:" Margin="5,5,5,0"/>
<ComboBox Margin="5,0,5,5" ItemsSource="{Binding Items, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" IsSynchronizedWithCurrentItem="True"
SelectedItem="{Binding SelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" BorderThickness="1" BorderBrush="White">
</ComboBox>
</StackPanel>
</DataTemplate>
I'd like to get the selected Item of a ComboBox using the MVVM Pattern (beginner).
I've read that this could be achieved by binding the SelectedItem Property to a Property in the ViewModel.
XAML:
<ComboBox ItemsSource="{Binding RoomLockerLinkCollection}"
DisplayMemberPath="Room.Name"
SelectedItem="{Binding SelectedRoom}"/>
ViewModel:
public Room SelectedRoom { get; set; }
But it's not working - the only thing thats happening is a appearing red border around that ComboBox - in addition after selecting a new item in the ComboBox the "SelectedRoom" property in my VM is still null.
Edit 1 :
One short additional question:
The binding works fine - at least for the top "category". My Wrapper-Class also contains a list of lockers.
<ComboBox DataContext="{Binding SelectedItem, ElementName=_cmbRoomSelection}" ItemsSource=" {Binding LockerCollection}" DisplayMemberPath="Name" SelectedValue="{Binding SAVM.SelectedLocker, Mode=TwoWay}" />
When I check the type of the SelectedValue it's a "Locker" - fine.
But the SelectedLocker-Property in my VM stays null...
Additional, could s.o. explain when to use "SelectedItem" and "SelectedValue"? What's the difference? Setting the DataContext in the xaml code above can not be done by binding the SelectedValue...
Edit 2 (Solution) :
Okay, got it!
As I figured out I've reset my DataContext - now the Property SAVM of course could not be found.
Solution:
<ComboBox DataContext="{Binding SelectedItem, ElementName=_cmbRoomSelection}"
ItemsSource="{Binding LockerCollection}"
DisplayMemberPath="Name"
SelectedValue="{Binding SAVM.SelectedLocker **ElementName=_vStorage**, Mode=TwoWay}" />
The red box is an indication of a validation error from your Binding ,
The most common error would be that the BindingSource and the BindingTarget are not of the same type.
Use SelectedValue and SelectedValuePath to bind to your Room object.
CS :
public class Room
{
public string RoomName { get; set; }
}
public class RoomWrapper
{
public Room Room { get; set; }
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
public List<RoomWrapper> RoomWrappers
{
get
{
var list = new List<RoomWrapper>();
for (int i = 0; i < 10; i++)
{
list.Add(new RoomWrapper { Room = new Room { RoomName = "Room " + i } });
}
return list;
}
}
private Room selectedRoom;
public Room SelectedRoom
{
get { return selectedRoom; }
set
{
selectedRoom = value;
}
}
XAML :
<ComboBox ItemsSource="{Binding RoomWrappers}"
DisplayMemberPath="Room.RoomName"
SelectedValuePath="Room"
SelectedValue="{Binding SelectedRoom, Mode=TwoWay}" />