I'm totally new to Xamarin Forms. I want to get the selected picker item Id when user is going to submit data. Data is populating to picker without any issue.
Model Class
public class CurrentStatus
{
public string id { get; set; }
public string current_status { get; set; }
}
public enum CurrentStatusId
{
NotApproved = 2,
Approved = 3,
Selected = 4,
NotSelected = 5
}
I bounded the ID like this in my view.
<local:HCImagePicker x:Name="currentstatus" Title="Current Status" SelectedIndexChanged="HandleStatusItemChanged" SelectedItem="{Binding CurrentStatusId}" ItemsSource="{Binding CurrentStatuses}" ItemDisplayBinding="{Binding current_status}" HorizontalOptions="FillAndExpand" Margin="0,0,0,10" Image="arrowdown" ImageAlignment="Right" ImageHeight="8" ImageWidth="12">
</local:HCImagePicker>
Just try to get the id after submiting data (In here simply I'm printing the value)
public async void SubmitData(object sender, EventArgs e){
var selectedId = currentstatus.SelectedItem;
await DisplayAlert("TEST", "Id is"+ selectedId, "OK");
}
Instead of getting a id (I don't want to get the selected value) , I'm getting a MyProject.Models.CurrentStatus.
Could someone help me to fix this.
Without actually knowing how your HCImagePicker looks like, I guess that SelectedItem is probably of type object.
Therefore you need to cast it and access it's properties correctly:
CurrentStatus selectedStatus = (currentStatus.SelectedItem as CurrentStatus);
if (selectedStatus == null)
return;
await DisplayAlert("TEST", "Id is"+ selectedStatus.id , "OK");
Personal note:
Do yourself a favour and don't use var. It might be comfortable and quick to use, however when it comes to casting around objects of any kind, you will definitely end up being confused, since you don't know what type of class you might end up with.
As far as I have experienced, using var instead of a proper type makes it harder to read and maintain your code in long term. Also you will be able to pinpoint possible error sources way faster when a cast goes wrong, because the compiler will warn you that what you attempt to do isn't possible.
Related
So I am trying to build a MVVM application with WPF and i am stuck with this problem:
In the view class, I have a list of textBoxes that I want to acces in order to validate the inputs.
So I managed to do this by writing in the view class, and it works:
var list = mainGrid.Children.OfType<TextBox>().ToList();
var dictOfTb = new Dictionary<string, string>();
foreach (var item in list)
{
dictOfTb.Add(item.Name, item.Text);
}
But the problem is that I am trying to respect the MVVM pattern and I should to this in a helper class, lets call it ModelsPageHelper, and access it from the View, because here what I am trying to do is to only get data from the UI and pass it to the viewModel to get a result, and then to display it.
So in this class I wrote a method,GetValuesFromTextBoxes(List<TextBox> textBoxes) and I am writing the same code, but now I get a message saying that TextBox does not contain a definition for Name.
So the question is, how can I do the same thing in the helper class to acces the names of those textBoxes?
To do MVVM properly you have to stop thinking about code to validate text boxes, and think about a ViewModel that validates itself, and a view that displays the state of the ViewModel.
For example, if you have a property called Foo which must have the value Bar, the code would look like this:
public string FooError { get; private set; }
private string foo;
public string Foo
{
get => return foo;
set
{
foo = value;
if (foo == "Bar") FooError = "";
else FooError = "Foo must be Bar";
NotifyPropertyChanged();
NotifyPropertyChanged(nameof(FooError));
}
}
and your XAML would look something like this:
<TextBox Text={Binding Foo}/>
<TextBlock Text={Binding FooError}/>
Then perhaps your save button could check for errors too.
I'd strongly advise you to look into INotifyDataErrorInfo, which is a great way to organise your errors in a way that WPF can easily display (instead of properties like FooError). It might seem like a lot of work the first time you use it, but it's great once you've got lots of controls and validation rules.
I want to implement validation on text box for whether the name exists in the database. I am using wpf with c#. I have implemented a validation on the text box while saving new data. My problem is in Edit Mode: when I go to edit mode and try to save, an error appears that the name already exist.
The Below Code works fine on save mode But when it comes to Edit mode when datas get binding the error message shows.
pls suggest me a good way to implement the validation that work on edit mode too.
class MyParent
{
public MyCarClass CurrentCarEntity {get; set;}
private void txtName_TextChanged(object sender, RoutedEventArgs e)
{
CurrentCarEntity.Name = txtName.Text.Trim();
var getName = //Code for getting name from local db
if(CurrentCarEntity.Name != Null)
{
if(getName.Equals(CurrentCarEntity.Name))
{
MessageBox.Show("Name Already Exists");
}
}
}
}
Looks like you're making validation fail for the entire form if the name already exists - validation will trigger every time you try to submit (edit, insert, etc) so edits will always fail.
I would make two textboxes, one for inserts and one for edits. Hide the insert box while in edit mode, or if you want to stick with one, at least disable the validator when editing.
It seems that you are following the wrong approach
let us assume we have a class called users like following
public class User: IValidatableObject
{
public int Id{get; set;}
public string UserName{get; set;}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if(string.IsNullOrEmpty(UserName))
yield return new ValidationResult("Username field is required!", new string[]{"UserName"});
else
{
// check if another User has the same username already
var db= new YourDbContext();
var exists=db.Users.FirstOrDefault(t=>t.Id!=Id && t.UserName.ToLower()=UserName.ToLower());
if(exists!=null)
yield return new ValidationResult("Username is already used by another user!", new string[]{"UserName"});
}
}
}
you don't need to worry about the edit or create, since in both cases you are checking the database if the Users table contains another user,and not same user you are creating or editing, has the same username.
hope this will help you
I avoided to ask this question, but the ListBox's selected index can no be set. I have read the other threads and applied the settings, but it doesn't work.
<ListBox ItemsSource="{Binding}"
HorizontalAlignment="Right"
Name="lstReading" Height="Auto"
SelectedIndex="{Binding BookmarkSelectedIndex}">
In the something.xaml.cs, I am settings
lstReading.DataContext = IQText;
Where, IQText is an IEnumerable<dictIQ> and includes the BookmarkSelectedIndex as data element. Other data elements from IQText can be used but the listindex can't be set. Could someone please let me know why?
Are you have BookmarkSelectedIndex inside of dictIQ class? So, you have one BookmarkSelectedIndex per item, not per collection!
You can create separate property BookmarkSelectedIndex outside of dictIQ or create class that inherited from ObservalbeCollection<dictIQ> and have additional property BookmarkSelectedIndex:
public class CollectionWithIndex: ObservalbeCollection<dictIQ>
{
public int BookmarkSelectedIndex { get; set; }
}
I hope you choose best solution suitable for you
use this code for select item at runtime...
List<Audio> lst = Audio.GetAudioFiles();
Audio aufile = new Audio { FileDisplayName = "Select Audio File" };
lst.Insert(0, aufile);
lstPickAudio.ItemsSource = lst;
string FileDisplayName="your condition"
lstPickAudio.SelectedItem = lst.Where(p => p.FileName == FileDisplayName).First();
I am trying to migrate a small prototype application I made in WinForms to WPF. I'm having some issues with a combobox in WPF not changing values when I select a different value from the drop-down. Initially, I tried just copying the code that I used in my WinForms app to populate the combobox and determine if a new index had been selected. This is how my WinForms code looked like:
private void cmbDeviceList_SelectedIndexChanged(object sender, EventArgs e)
{
var cmb = (Combobox) sender;
var selectedDevice = cmb.SelectedItem;
var count = cmbDeviceList.Items.Count;
// find all available capture devices and add to drop down
for(var i =0; i<count; i++)
{
if(_deviceList[i].FriendlyName == selectedDevice.ToString())
{
_captureCtrl.VideoDevices[i].Selected = true;
break;
}
}
}
Earlier in the code, I am populating the _deviceList List and the combo box (in Form1_Load to be specific) by looping over the available devices and adding them. I tried the same approach in WPF and could only populate the combo box. When I selected a new value, for some reason the same exact value (the initial device) was being sent into the event code (cmbCaptureDevices_SelectionChanged in my WPF app). I looked around for some tutorials in WPF and found that maybe data binding was my issue, and I tried that out instead. This is my combobox in my XAML file:
<ComboBox ItemsSource="{Binding Devices}" Name="cmbCaptureDevices"
IsSynchronizedWithCurrentItem="True" SelectedItem="{Binding CurrentDevice,
Mode=TwoWay}" Se;ectionChanged="cmbCapturedDevices_SelectionChanged" />
There's more to that XAML definition, but it's all arbitrary stuff like HorizontalAlignment and whatnot. My VideoDevicesViewModel inherits from INotifyPropertyChanged, has a private List<Device> _devices and a private Device _currentDevice. The constructor looks like:
public VideoDevicesViewModel()
{
_devices = GetCaptureDevices();
DevicesCollection = new CollectionView(_devices);
}
GetCaptureDevices simply is the loop that I had in my WinForms app which populates the list with all avaialble capture devices on the current machine. I have a public CollectionView DevicesCollection { get; private set; } for getting/setting the devices at the start of the application. The property for my current device looks like:
public Device CurrentDevice
{
get { return _currentDevice; }
set
{
if (_currentDevice = value)
{
return;
}
_currentDevice = value;
OnPropertyChanged("CurrentDevice");
}
}
OnPropertyChanged just raises the event PropertyChanged if the event isn't null. I'm new to WPF (and pretty new to C# in general, honestly) so I'm not sure if I'm missing something elementary or not. Any idea as to why this combobox won't change values for me?
Discovered the answer on my own here. The unexpected behavior was a result of using the Leadtools Device class. It's a COM component and apparently was not playing nicely with my application. I honestly don't understand why exactly it worked, but I wrapped the Device class in another class and used that instead. As soon as I was using the wrapper class, the combo box functioned as it should.
You are using the assignment operator '=' instead of the equality operator '=='
Change
if (_currentDevice = value)
to
if (_currentDevice == value)
Try the following
if _currentDevice == value ...
Ok, I'm no newbie at programming or C# as such, I just can't seem to get WPF's databinding straight in my head. My colleagues are raving about it (and yes, I will ask them as well) but right now I'm stumped.
Here's what I'd like to do for starters:
As an example I've got a list of things like this:
List<Thing> thingList = Source.getList();
Now normally I'd go
foreach(Thing t in thingList)
{
//add thing to combobox
}
But from what I can gather is that I can somehow not do this but use a databinding to populate the combobox for me.
What I can't seem to get is where do I put the 'thingList'? Do I make it a separate property somewhere? Where do I put that property?
I feel very stupid at the moment, as I've been struggling with this for a while now and I can't find any examples out there that make me understand this - probably very simple - concept.
Anyone out there willing to help me or point me at some step-by-step guide I might have missed?
Use ObservableCollection<T> for databinding in WPF. Where T is your class. See example below
public class NameList : ObservableCollection<PersonName>
{
public NameList() : base()
{
Add(new PersonName("A", "E"));
Add(new PersonName("B", "F"));
Add(new PersonName("C", "G"));
Add(new PersonName("D", "H"));
}
}
public class PersonName
{
private string firstName;
private string lastName;
public PersonName(string first, string last)
{
this.firstName = first;
this.lastName = last;
}
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
public string LastName
{
get { return lastName; }
set { lastName = value; }
}
}
Now in XAML. Go to resource tag
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:SDKSample"
x:Class="Sample.Window1"
Width="400"
Height="280"
Title="MultiBinding Sample">
<Window.Resources>
<c:NameList x:Key="NameListData"/>
</Window.Resources>
<ListBox Width="200"
ItemsSource="{Binding Source={StaticResource NameListData}}" // Name list data is declared in resource
ItemTemplate="{StaticResource NameItemTemplate}"
IsSynchronizedWithCurrentItem="True"/>
xmnls:c will give you option to choose the class. Here you can choose c,d ,e x whatever but be sure it should be used earlier
When it comes to data-binding i've found that this page if read thoroughly answers most of the questions beginners have about it.
To answer the question: The alernative to adding all the items is to tell the ComboBox where to get its items from, this is done with the ItemsSource property.
You can either set this in XAML or in code, while you would need a binding expression in XAML a normal assignment should do in code:
comboBox.ItemsSource = thingList;
If you do not specify further how those objects in the list are to be displayed the ToString method will be called, unless overridden you will usually end up with the class-path of your object. There are two main ways of telling the application how to display the object:
The fist and more heavy option is Data Templates which is used for displaying complex data using controls (which in turn can have items and templates etc), the second way is using lightweight properties like DisplayMemberPath, which should be set to the name of the property which should be displayed (usually just a string).
If your list changes and the combo box should be updated on its own the source should implement INotifyCollectionChanged, in most cases you will use the standard implementation ObersableCollection<T>.
Most people would use WPF Databinding to populate the combobox but you don't have to.
You can add the items via code if you want to.
It's easy to get trapped into doing everything as it "should" be done without have a good reason for doing it that way (BTW, I'm not recommending you manually add the items, I'm just saying you can).
List<Thing> thingList = Source.getList();
foreach(Thing t in thingList)
{
combobox.Items.Add( t );
}