So I enabled a rightclick option for my DataGrid. I want to display just one property of the selecteditem but it's not behaving like how I would like. It displays my namespace and extra.
public class Paymentinfo
{
public int PaymentNo { get; set; }
public String Date { get; set; }
public double Payment { get; set; }
public double Principle { get; set; }
public double Interest { get; set; }
public double Balance { get; set; }
}
private void MenuItem_OnClick(object sender, RoutedEventArgs e)
{
MessageBox.Show(AmortGrid.SelectedItem.ToString());
}
I am trying to implement this without using a viewmodel! If I put a breakpoint where Messagebox is and put the cursor over the selectedItem then it'll display the properties paymentNo-date-payment-principle-interest-balance. The only value I require is PaymentNo
was hoping it'd be something like this
MessageBox.Show(AmortGrid.SelectedItem.PaymentNo.ToString());
When you call ToString() like that, you get the name of the class type, which is what you're seeing.
If that's a collection of Paymentinfo, cast SelectedItem back to that type first:
MessageBox.Show(((Paymentinfo)AmortGrid.SelectedItem).PaymentNo.ToString());
FWIW, I'd reconsider the ViewModel. Far easier to test your code if you get it out of the code-behind.
You'd be able to bind your SelectedItem directly to a property in the ViewModel (perhaps called SelectedPaymentinfo), and then there'd be no messing around with casting.
You can also set the SelectedValuePath and instead of using SelectedItem use SelectedValue.
Create a ToString() method on PaymentInfo.
public class Paymentinfo
{
public override string ToString()
{
return PaymentNo.ToString();
}
}
Related
Maybe the question is a little bit dumb, but I did not quite find solution anywhere else.
So I am using a BindingList of custom made class objects as a DataSource for DataGridView.
Everything works fine with properties, that are directly inherited from other classes, but if I have an object of other class in the main class, its properties wont show up in DataGridView.
Classes are:
enum Valsts
{
Latvija,
Igaunija,
Ķīna,
ASV
}
class Razotajs
{
public Valsts valsts { get; set; }
public string razotajaNosaukums { get; set; }
}
class Tehnika
{
public string krasa { get; set; }
public Razotajs razotajs = new Razotajs();
}
class Viedierice : Tehnika
{
public string operetajsistema { get; set; }
public double ekranaIzmers { get; set; }
public bool irHDMI { get; set; }
}
class MobilaisTelefons : Viedierice
{
public string modelis { get; set; }
public double svars { get; set; }
public SimKarte sim = new SimKarte();
public override string ToString()
{
return String.Join(";", modelis.ToString(),svars.ToString(),sim.veids.ToString(),operetajsistema.ToString(),ekranaIzmers.ToString(),irHDMI.ToString(),krasa.ToString(),razotajs.razotajaNosaukums.ToString(),
sim.numurs.ToString(),razotajs.valsts.ToString());
}
}
class SimKarte
{
public string veids { get; set;}
public int numurs { get; set; }
}
For example- I can see columns "modelis" and "svars", but attributes like "veids" and "numurs" from class SimKarte are not included in the DataGridView.
Is there any solution for this?
I've tried to add { get; set; } after declaring a new instance of an object in the class, but it's not even a real thing. I really don't have any idea, what would help me to solve this.
Thank you all in advance! :)
Honestly, I think the simplest solution is the one JohnG proposed; add proxy properties to your main class that read/write the properties of the complex objects
A datagridview will show only the simple types it knows how to show, from the top level class. It will not dig into properties of properties (otherwise even adding a string column would cause the grid to fill up with a Length column an Isinterned column etc..)
partial class MobilaisTelefons : Viedierice
{
public string modelis { get; set; }
public double svars { get; set; }
public SimKarte sim { get; set; } = new SimKarte();
public override string ToString()
{
return String.Join(";",
modelis, svars, sim.veids, operetajsistema, ekranaIzmers, irHDMI, krasa, razotajs.razotajaNosaukums,
sim.numurs, razotajs.valsts);
}
}
partial class MobilaisTelefons {
public string SimVeids { get => sim.veids; set => sim.veids = value; }
public string SimNumers { get => sim.numers; set => sim.numers = value; }
public string RazotajsRazotajaNosaukums { get => razotajs.razotajaNosaukums; set => razotajs.razotajaNosaukums = value; }
public Valsts RazotajsValsts { get => razotajs.valsts; set => razotajs.valsts = value; }
}
Few tips:
I made the extension of the class partial so you can put it in another file. Hiding its members from intellisense would be hard work
the Enum column will probably show as an int. if you want it to be sensible, use a DataGridViewComboBox column bound to a list of all the enum values/names. On the column, set the DataMember to "RazotajsValsts", the DataSource to the list of enums, the DisplayMember to the property representing the enum name and the ValueMember to the property representing the enum value. See Enum.GetValues.
Enums should only have a plural name (if valsts is plural) if they are flags
classes should not have a plural name
public properties names should be in PascalCase not camelCase
I simplified your tostring: you don't need to call to string on everything; string join will do it. You especially don't need to call tostring on a string
I have a RealmObject extended class. Some of its properties are declared like this:
public String Name { get; set; }
public int Age { get; set; }
These are stored in the Realm data base when I save the object. For the other one, I need a custom setter like this:
private double _amount;
public double Amount
{
get { return _amount; }
set
{
_amount = value;
NotifyPropertyChanged();
}
}
And this one isn't stored. Can someone help me?
Thanks by advance
Realm will only store properties that have an automatic getter and setter, because it uses compile-time IL generation to replace the implementation with calls into the database. If you want to customize the getter and setter, you need to define your fields as properties instead:
private double _amount { get; set; }
public double Amount
{
get { return _amount; }
set { ... }
}
Realm will use the _amount property, while your app can use the public Amount one.
On a side note, you don't need to call NotifyPropertyChanged for persisted properties because Realm already handles INotifyPropertyChanged events for you. So public double Amount { get; set; } is enough to raise property changed notifications.
I have a ReactiveObject class named GroupedIssueTypeSearchFilter:
public class GroupedIssueTypeSearchFilter : ReactiveObject
{
public int CategoryID { get; set; }
public string CategoryTitle { get; set; }
int selectionCode;
public int SelectionCode
{
get { return selectionCode; }
set { this.RaiseAndSetIfChanged(ref selectionCode, value); }
}
public ReactiveList<IssueTypeSearchFilter> IssueTypeFilterList { get; set; }
public GroupedIssueTypeSearchFilter(int catID, string catTitle, List<IssueTypeSearchFilter> issueTypeList)
{
this.CategoryID = catID;
this.CategoryTitle = catTitle;
this.IssueTypeFilterList = new ReactiveList<IssueTypeSearchFilter>(issueTypeList);
}
}
Then in IssueTypeFilterTableViewDelegate class, one of its properties is GroupedIssueTypesFilter:
public ReactiveList<GroupedIssueTypeSearchFilter> GroupedIssueTypesFilter { get; set; }
I also have a ViewController and its ViewModel. I used BindTo to bind one of the view model properties to that class:
IssueTypeFilterTableViewDelegate tvd = new IssueTypeFilterTableViewDelegate();
this.WhenAnyValue(vm => vm.ViewModel.GroupedStandardIssueTypesTV)
.BindTo(this, x => x.tvd.GroupedIssueTypesFilter);
GroupedStandardIssueTypesTV type is ReactiveList<GroupedIssueTypeSearchFilter>.
Later, with some operation inside the ViewModel, the GroupedStandardIssueTypesTV.IssueTypeFilterList value is changed.
But, GroupedIssueTypesFilter.IssueTypeFilterList value is not changed. I need to close the View, re-open it, then its value will be updated.
How to make the GroupedIssueTypesFilter follows the changes in GroupedStandardIssueTypesTV?
Fully agree with #ramonesteban78 answer.
But you can go in another way and refill your ReactiveList with Clear and Add/AddRange methods.
Like this:
public GroupedIssueTypeSearchFilter(int catID, string catTitle, List<IssueTypeSearchFilter> issueTypeList)
{
this.CategoryID = catID;
this.CategoryTitle = catTitle;
this.IssueTypeFilterList.Clear();
this.IssueTypeFilterList.AddRange(issueTypeList);
}
Don't forget read about 'Suppressing Notifications' here: https://docs.reactiveui.net/en/user-guide/lists/index.html
I think your problem is that you are not raising changes in your ReactiveList declaring it like:
public ReactiveList<IssueTypeSearchFilter> IssueTypeFilterList { get; set; }
Try this:
ReactiveList<IssueTypeSearchFilter> issueTypeFilterList
public ReactiveList<IssueTypeSearchFilter> IssueTypeFilterList
{
get { return issueTypeFilterList; };
set { this.RaiseAndSetIfChanged(ref issueTypeFilterList, value); };
}
Without the RaiseAndSetIfChanged you won`t be communicating changes to the view in your collection.
You can also subscribe to the observables that ReactiveList exposes like:
ItemsAdded
ItemsRemoved
ItemsMoved
Changed
etc
More info here: https://docs.reactiveui.net/en/user-guide/lists/index.html
I have a WPF app that uses a DataGrid element that is bound to an ObservableCollection:
ObservableCollection<Deployment> deployments = DataAccess.GetDeployments();
datagrid01.ItemsSource = deployments;
Here is a Deployment class. A Deployment object represents an Windows installation that is happening out in the field, so things like CurrentTime changes frequently.
class Deployment
{
public string UniqueID { get; set; }
public string ComputerName { get; set; }
public string IPAddress { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public string UserName { get; set; }
public string TaskSequenceName { get; set; }
public string MachineObjectOU { get; set; }
public DateTime? StartTime { get; set; }
public DateTime? CurrentTime { get; set; }
public string Status { get; set; }
}
Periodically I want to update DataGrid with any changes to these objects, like the CurrentTime or their Status. Their Status changes to a 3 when they are finished installing, or is a 2 if they are in an error state.
So, I can get a new collection of objects on a timer, but what is the best way to add ONLY the new items from this new collection to the DataGrid, and to CHANGE the existing items who have an updated time, or status, or whatever?
use .Except() method to filter items from ObservableCollection
newRecordings = recordings.Except(_recordingsHidden).ToList();
A method like shown below should do the job. It does not replace an existing item in the deployments collection, but just updates the properties of the item.
In order to make this update the UI, the Deployment class would have to implement the INotifyPropertyChanged interface and raise the PropertyChanged when a property value changes. A simple solution would be to add an UpdateFrom(Deployment d) method, which updates all properties and then raises a single PropertyChanged event with null as property name (to notify that all properties have changed).
using System.Linq;
...
private void UpdateDeployments(IEnumerable<Deployment> changedDeployments)
{
foreach (var changedDeployment in changedDeployments)
{
var deployment = deployments.FirstOrDefault(
d => d.UniqueID == changedDeployment.UniqueID);
if (deployment != null)
{
deployment.UpdateFrom(changedDeployment);
}
else
{
deployments.Add(changedDeployment);
}
}
}
Make your ObservableCollection<Deployment> as property and Raise a property change event on that using INotifyPropertyChange interface. You can then bind this property to your itemsource of DataGrid in xaml itself. Hence any change in your collection either from UI or from Database, whole list is refreshed automatically.
I'm using a VB power packs data repeater control. I need to bind a list of custom objects to labels inside the repeater. The following code works except for the Tip.User.UserName binding.
How can I bind to a property of an Inner class like Tip.User.UserName
public interface ITip
{
DateTime Date { get; set; }
int Id { get; set; }
int UserId { get; set; }
User User { get; set; }
Group Group { get; set; }
}
public interface IUser
{
string DisplayName { get; set; }
string UserName { get; set; }
}
List<Tip> currentTips = SearchTips(toolTxtSearch.Text, Convert.ToInt32(toolCmbTipGroups.ComboBox.SelectedValue));
lblTipId.DataBindings.Add(new Binding("Text", currentTips, "Id"));
lblTipUser.DataBindings.Add(new Binding("Text", currentTips, "User.UserName")); // this line doesnot work !!!
repeater.DataSource = currentTips;
Totally depends on what error you're getting because nested property syntax should work for WinForm bindings.
As a work around (maybe the DataRepeater isn't using regular binding mechanisms?) add a property to you ITip implementation to wrap it up:
public string UsersUserName
{
get { return User != null ? User.UserName : null; }
}
Edit: Also don't forget to implement INotifyPropertyChanged on your data objects if you want bindings to update when values do. In this case, send property changed events for both the User property of ITip implementations and the UserName of the IUser implementation.