I have a collection ConversationModel that have two models in it. I want to get the value of the very first item of the collection. I tried this code var obj = ConversationCollection.First(); to get the value but it always returns null. Here the model's property is populated but outside of it. How can I get those values
public class ConversationModel
{
public SendMessageModel SMM { get; set; }
public ReceivedMessageModel RMM { get; set; }
}
Here's how I create my collection:
ObservableCollection<ConversationModel> cm = new ObservableCollection<ConversationModel>();
foreach (DataRow convo in dataTable.Rows)
{
string _messageID = (string)convo["MessageID"];
string message = (string)convo["UserMessage"];
string username = (string)convo["FromUser"];
DateTime datetime = (DateTime)convo["MessageDateTime"];
string messageStatus = (string)convo["MessageStatus"];
string mdt = "";
if (datetime.Date == DateTime.Now.Date) mdt = datetime.ToString("t");
if (username == ClientUsername)
{
SendMessageModel smm = new SendMessageModel
{
MessageIdentifier = _messageID,
UserDisplayName = ClientDisplayName,
SendMessage = message,
MessageTime = mdt,
MessageStatus = (Status)Enum.Parse(typeof(Status), messageStatus)
};
cm.Add(new ConversationModel { SMModel = smm });
}
else
{
ReceivedMessageModel rmm = new ReceivedMessageModel
{
MessageIdentifier = _messageID,
ClientDisplayName = RecipientDisplayName,
MessageTime = mdt,
ReceivedMessage = message
};
cm.Add(new ConversationModel { RMM = rmm });
}
}
ConversationCollection = cm;
My WPF Code for the ItemsControl
<ItemsControl ItemsSource="{Binding ConversationCollection}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type Models:ReceivedMessageModel}">
<UserControls:RecievedMessageBubble/>
</DataTemplate>
<DataTemplate DataType="{x:Type Models:SendMessageModel}">
<UserControls:SendMessageBubble />
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
If ConversationCollection is an ObservableCollection<ConversationModel>, it can only contain ConversationModels which is why neither of your data templates will be applied.
If you change its type to for example ObservableCollection<object>, you could add both ReceivedMessageModels and SendMessageModels to it.
You will then be able to retrive the first item by casting:
var receivedMessageModel = ConversationCollection[0] as ReceivedMessageModel;
if (receivedMessageModel != null)
{
//the first item is a ReceivedMessageModel
}
else
{
var sendMessageModel = ConversationCollection[0] as SendMessageModel;
// the first item is A SendMessageModel
}
Related
I have a JSON, like the image below:
I have a ComboBox inside a ListView. I want to display "pairs" in the ComboBox.
XAML:
<ListView Name="ListPairOption">
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:PairClass">
<StackPanel
x:Name="pilganStack">
<WebView
x:Name="option"
local:MyProperties.HtmlString="{Binding Name}"/>
</StackPanel>
<ComboBox
x:Name="pairOption"
DisplayMemberPath="NameA"
SelectedValue="{Binding ComboBoxClass, Mode=TwoWay}"
ItemsSource="{x:Bind PilihanS}"
PlaceholderText="Pilih" />
</ListView
Codes:
try
{
JsonObject jsonObject = JsonObject.Parse(jsonText);
JsonObject questionObject = jsonObject["EXAM_QUESTION"].GetObject();
ObservableCollection<PairClass> itemL = new ObservableCollection<PairClass>();
JsonArray mapArray = questionObject["map"].GetArray();
foreach (JsonValue mapValue in mapArray)
{
JsonArray mapArrayI = mapValue.GetArray();
PairClass pair = new PairClass();
foreach (JsonValue mapValueI in mapArrayI)
{
try
{
string v = mapValueI.ToString();
pair.Name = v;
}
}
}
itemL.Add(pair);
}
JsonArray pairArray = questionObject["pairs"].GetArray();
string pairString = "";
foreach (JsonValue pairValue in pairArray)
{
JsonArray pairArrayI = pairValue.GetArray();
List<ComboBoxClass> PilihanS = new List<ComboBoxClass>();
foreach (JsonValue pairValueI in pairArrayI)
{
try
{
var collection = Regex.Matches(v, "\\\"(.*?)\\\"");
foreach (var item in collection)
{
string v3 = item.ToString().Trim('"');
pairString = v3;
}
}
}
PilihanS.Add(new ComboBoxClass() { NameA = pairString });
}
ListPairOption.ItemsSource = itemL;
}
PairClass:
public class PairClass
{
public string Name { get; set; }
public ObservableCollection<ComboBoxClass> PilihanS { get; set; }
public PairClass(string name)
{
Name = name;
}
}
public class ComboBoxClass
{
public string NameA { get; set; }
public override string ToString()
{
return this.NameA;
}
}
}
From the code above, I didn't succeed in displaying it into a ComboBox in a ListView so that the ComboBox is empty, as shown below:
How to display it into a ComboBox?
From the code above, I didn't succeed in displaying it into a ComboBox in a ListView so that the ComboBox is empty, as shown below:
If you want to access the property that out of DataType, please using Binding ElementName=Control Name then access the the outside property from the parent DataContext. please note you need to set current page DataContext as this this.DataContext = this;. it could make sure you can access PilihanS where in the code behind from DataTemplate.
<ComboBox
x:Name="pairOption"
ItemsSource="{Binding DataContext.PilihanS, ElementName=ListPairOption}"
PlaceholderText="Pilih" />
code behind
public MainPage()
{
this.InitializeComponent();
this.DataContext = this;
}
public List<ComboBoxClass> PilihanS { get; set;} = new List<ComboBoxClass>();
And delete this line where in your code above List<ComboBoxClass> PilihanS = new List<ComboBoxClass>();
I have 2 comboboxes with 2 categories and 2 types of subcategories. Combobox categories contain "Usia" and "Serial". Whereas the subcategory combobox retrieves data from the json server. If you select "Usia" in the combobox category, it will display "ratingBox" in the combobox subcategory, and if you select "Serial" in the combobox category, it will display "serialBox" in the combobox subcategory.
XAML:
<StackPanel x:Name="comboBoxStack" Grid.Row="0" Margin="30,15,0,0" Orientation="Horizontal">
<ComboBox x:Name="kategoriBox" PlaceholderText="Kategori" FontSize="14" SelectionChanged="KategoriBox_SelectionChanged">
<ComboBoxItem>Usia</ComboBoxItem>
<ComboBoxItem>Serial</ComboBoxItem>
</ComboBox>
<ComboBox x:Name="ratingBox" Margin="15,0,0,0" PlaceholderText="Pilih Usia" ItemsSource="{x:Bind RatingList}" FontSize="14" SelectionChanged="RatingBox_SelectionChanged"/>
<ComboBox x:Name="serialBox" Margin="15,0,0,0" PlaceholderText="Pilih Serial" ItemsSource="{x:Bind SerialList}" FontSize="14" SelectionChanged="SerialBox_SelectionChanged"/>
</StackPanel>
Code:
List<Rating> RatingList = new List<Rating>();
List<Serial> SerialList = new List<Serial>();
string umurID = "";
string serialID = "";
private void KategoriBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var comboBoxItem = kategoriBox.Items[kategoriBox.SelectedIndex] as ComboBoxItem;
if (comboBoxItem != null)
{
string selectedcmb = comboBoxItem.Content.ToString();
if(selectedcmb == "Usia")
{
ratingBox.Visibility = Visibility.Visible;
serialBox.Visibility = Visibility.Collapsed;
RatingC();
ratingBox.SelectedIndex = -1;
}
else if(selectedcmb == "Serial")
{
ratingBox.Visibility = Visibility.Collapsed;
serialBox.Visibility = Visibility.Visible;
SerialC();
serialBox.SelectedIndex = -1;
}
}
else
{
ratingBox.Visibility = Visibility.Visible;
serialBox.Visibility = Visibility.Collapsed;
RatingC();
}
}
private async void RatingC()
{
serialID = "";
ConnectionProfile connections = NetworkInformation.GetInternetConnectionProfile();
if (connections != null && connections.GetNetworkConnectivityLevel() == NetworkConnectivityLevel.InternetAccess)
{
try
{
string urlPath = "https://..../Fetch/rating";
var httpClient = new HttpClient(new HttpClientHandler());
var values = new List<KeyValuePair<string, string>>
{
};
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("SCH-API-KEY", "SCH_KEnaBiDeplebt");
var response = await httpClient.PostAsync(urlPath, new FormUrlEncodedContent(values));
response.EnsureSuccessStatusCode();
if (!response.IsSuccessStatusCode)
{
busyindicator.IsActive = false;
}
string jsonText = await response.Content.ReadAsStringAsync();
JsonObject jsonObject = JsonObject.Parse(jsonText);
JsonArray jsonData = jsonObject["data"].GetArray();
foreach (JsonValue groupValue in jsonData)
{
JsonObject groupObject1 = groupValue.GetObject();
string id = groupObject1["id"].GetString();
string name = groupObject1["rating"].GetString();
Rating rate = new Rating();
rate.ID = id;
rate.Name = name;
RatingList.Add(new Rating()
{
ID = rate.ID,
Name = rate.Name
});
}
}
catch
{
busyindicator.IsActive = false;
}
}
}
private async void SerialC()
{
umurID = "";
ConnectionProfile connections = NetworkInformation.GetInternetConnectionProfile();
if (connections != null && connections.GetNetworkConnectivityLevel() == NetworkConnectivityLevel.InternetAccess)
{
try
{
string urlPath = "https://.../Fetch/serial";
var httpClient = new HttpClient(new HttpClientHandler());
var values = new List<KeyValuePair<string, string>>
{
};
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("SCH-API-KEY", "SCH_KEnaBiDeplebt");
var response = await httpClient.PostAsync(urlPath, new FormUrlEncodedContent(values));
response.EnsureSuccessStatusCode();
if (!response.IsSuccessStatusCode)
{
busyindicator.IsActive = false;
}
string jsonText = await response.Content.ReadAsStringAsync();
JsonObject jsonObject = JsonObject.Parse(jsonText);
JsonArray jsonData = jsonObject["data"].GetArray();
foreach (JsonValue groupValue in jsonData)
{
JsonObject groupObject1 = groupValue.GetObject();
string id = groupObject1["id"].GetString();
string name = groupObject1["nama"].GetString();
string slug = groupObject1["slug"].GetString();
Serial seri = new Serial();
seri.ID = id;
seri.Name = name;
seri.Slug = slug;
SerialList.Add(new Serial()
{
ID = seri.ID,
Name = seri.Name,
Slug = seri.Slug
});
}
}
catch
{
busyindicator.IsActive = false;
}
}
}
private void RatingBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(ratingBox.SelectedIndex != -1)
{
var comboBox = sender as ComboBox;
Rating value = comboBox.SelectedItem as Rating;
umurID = value.ID;
}
}
private void SerialBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(serialBox.SelectedIndex != -1)
{
var comboBox = sender as ComboBox;
Serial value = comboBox.SelectedItem as Serial;
serialID = value.ID;
}
}
Rating.cs:
class Rating
{
public string ID { get; set; }
public string Name { get; set; }
public override string ToString()
{
return this.Name;
}
}
Serial.cs same as Rating.cs
I have a problem, that is if I have chosen "Usia" in the combobox category, then the combobox subcategory for that category can display data on the server smoothly, then I choose "Serial" in the combobox category, eating combobox subcategories for that category can display data on the server with smoothly. Then I again chose "Usia" in the combobox category, so when I click on the subcategory, an error message appears as shown below:
And here is my PC screen video for my problem:
https://1drv.ms/v/s!Auqiv8Ukng7UgP8Fj-LMDa9skV3yfA
In the video when re-selecting "Usia" in the combobox category (at the end of the video), it will display an error message as shown above
How to handle it?
I have main combobox (Categories) and depended combobox (Subcategories). I want it to display SelectedItems when window opens. All works fine in .Net 4.0, but it doesn't work in .Net 4.5. I have two computeres with these .Net versions.
In .net 4.5. only main combobox displays SelectedItem, depended doesn't. How can I fix it?
I made test project to all of you who're interested, just copy and paste. I have no idea how I can make it smaller, sry. But it is simple, clear code example 100% generates the problem.
XAML:
<Window x:Class="GridTest.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Converter="clr-namespace:GridTest"
Title="TestWindow"
Height="300"
Width="300">
<Window.Resources>
<Converter:CategoryConverter x:Key="CategoryConverter"/>
</Window.Resources>
<Grid>
<DataGrid Name="_dataGrid"
CanUserAddRows="False"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ComboBox Grid.Column="0"
Name="_categories"
ItemsSource="{Binding Categories}"
DisplayMemberPath="Name"
SelectedItem="{Binding SelectedCategory, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
</ComboBox>
<ComboBox Grid.Column="1"
SelectedItem="{Binding SelectedSubcategory, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="Name">
<ComboBox.ItemsSource>
<MultiBinding Converter="{StaticResource CategoryConverter}">
<Binding Path="Subcategories"/>
<Binding Path="SelectedItem"
ElementName="_categories"/>
</MultiBinding>
</ComboBox.ItemsSource>
</ComboBox>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Code:
public class CategoryConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values[0] == null) return null;
var subcategories = values[0] as List<Subcategory>;
if (subcategories == null) return null;
var category = values[1] as Category;
if (category == null) return subcategories;
return subcategories.Where(g => g.CategoryId == category.Id);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public enum CategoryKinds
{
Car = 0,
Fruit = 1,
}
public class Category
{
public Int32 Id { get; set; }
public String Name { get; set; }
public override Boolean Equals(object obj)
{
var c = obj as Category;
if (c == null) return false;
return Id == c.Id;
}
}
public class Subcategory
{
public Int32 Id { get; set; }
public String Name { get; set; }
public Int32 CategoryId { get; set; }
public override Boolean Equals(object obj)
{
var sc = obj as Subcategory;
if (sc == null) return false;
return Id == sc.Id;
}
}
public class DataGridItem
{
public List<Category> Categories { get; set; }
public Category SelectedCategory { get; set; }
public List<Subcategory> Subcategories { get; set; }
public Subcategory SelectedSubcategory { get; set; }
public DataGridItem()
{
Categories = new List<Category>
{
new Category
{
Id = (Int32)CategoryKinds.Car, Name = "Car"
},
new Category
{
Id = (Int32)CategoryKinds.Fruit, Name = "Fruit"
}
};
Subcategories = new List<Subcategory>
{
new Subcategory
{
Id = 1,
Name = "Volvo",
CategoryId = (Int32) CategoryKinds.Car
},
new Subcategory
{
Id = 2,
Name = "Nissan",
CategoryId = (Int32) CategoryKinds.Car
},
new Subcategory
{
Id = 3,
Name = "Banana",
CategoryId = (Int32)CategoryKinds.Fruit
},
new Subcategory
{
Id = 4,
Name = "Lemon",
CategoryId = (Int32)CategoryKinds.Fruit
},
};
}
}
/// <summary>
/// Interaction logic for TestWindow.xaml
/// </summary>
public partial class TestWindow : Window
{
public List<DataGridItem> GridItems { get; set; }
public TestWindow()
{
InitializeComponent();
DataContext = this;
GridItems = new List<DataGridItem>
{
new DataGridItem
{
SelectedCategory = new Category
{
Id = (Int32)CategoryKinds.Car, Name = "Car"
},
SelectedSubcategory = new Subcategory
{
Id = 2,
Name = "Nissan",
CategoryId = (Int32) CategoryKinds.Car
}
},
new DataGridItem
{
SelectedCategory = new Category
{
Id = (Int32)CategoryKinds.Fruit, Name = "Fruit"
},
SelectedSubcategory = new Subcategory
{
Id = 4,
Name = "Lemon",
CategoryId = (Int32) CategoryKinds.Car
}
}
};
_dataGrid.ItemsSource = GridItems;
}
}
UPDATE
With approach suggested by Ilan and charly_b code will work fine.
GridItems = new List<DataGridItem>
{
new DataGridItem(),
new DataGridItem()
};
GridItems[1].SelectedCategory = GridItems[1].Categories[0];
GridItems[1].SelectedSubcategory = GridItems[1].Subcategories[1];
GridItems[0].SelectedCategory = GridItems[0].Categories[1];
GridItems[0].SelectedSubcategory = GridItems[0].Subcategories[3];
This code will result to:
Fruit - Lemon
Car - Nissan
But I have solution that will work even if you set SelectedItem that don't belong to ItemsSource of Combobox. You can override GetHashCode method like this:
public override int GetHashCode()
{
return Name.GetHashCode();
}
Obviously, in .Net 4.5 some of WPF methods operating with searching SelectedItem in Combobox's ItemsSource have different implementation from .Net 4.0 and now they use GetHashCode method :)
Try the next changes, the best practice is to use the source collection items in order to define the selected item. Firstly it is an architectural error to use a new item to define the selection (in both 4.5 and 4 dot.net versions). And second I advice you to use the mvvm approach (including INotifyPropertyChange implementation) to develop wpf related applications, and then all selection logic have to be moved to ViewModel and separated from the code behind (xaml.cs files).
public MainWindow()
{
InitializeComponent();
DataContext = this;
var f = new DataGridItem();
var firstselectedCategory = f.Categories.FirstOrDefault();
if (firstselectedCategory != null)
{
f.SelectedCategory = firstselectedCategory;
f.SelectedSubcategory =
f.Subcategories.FirstOrDefault(subcategory => subcategory.CategoryId == firstselectedCategory.Id);
}
else
{
f.SelectedCategory = null;
f.SelectedSubcategory = null;
}
var s = new DataGridItem();
var secondSelectedCategory = s.Categories.FirstOrDefault(category => !Equals(category, f.SelectedCategory));
if (secondSelectedCategory != null)
{
s.SelectedCategory = secondSelectedCategory;
s.SelectedSubcategory =
s.Subcategories.FirstOrDefault(subcategory => subcategory.CategoryId == secondSelectedCategory.Id);
}
else
{
s.SelectedCategory = null;
s.SelectedSubcategory = null;
}
GridItems = new List<DataGridItem>
{
f,s,
};
#region
//GridItems = new List<DataGridItem>
//{
// new DataGridItem
// {
// SelectedCategory = new Category
// {
// Id = (Int32) CategoryKinds.Car,
// Name = "Car"
// },
// SelectedSubcategory = new Subcategory
// {
// Id = 2,
// Name = "Nissan",
// CategoryId = (Int32) CategoryKinds.Car
// }
// },
// new DataGridItem
// {
// SelectedCategory = new Category
// {
// Id = (Int32) CategoryKinds.Fruit,
// Name = "Fruit"
// },
// SelectedSubcategory = new Subcategory
// {
// Id = 4,
// Name = "Lemon",
// CategoryId = (Int32) CategoryKinds.Fruit
// }
// }
//};
#endregion
_dataGrid.ItemsSource = GridItems;
}
The xaml code was not changed.
How it looks like:
.
I'll be glad to help if will have problems with the code.
Regards.
The Combobox SelectedItem object must be contained inside the Combobox's ItemsSource List.
In order to make your Programm work you can replace the SelectedSubCategory Property with the following code: (I would not use it like this in the production code, but it demonstrates how it works)
private Subcategory SelectedSubcategoryM;
public Subcategory SelectedSubcategory
{
get
{
return this.SelectedSubcategoryM;
}
set
{
this.SelectedSubcategoryM = (from aTest in this.Subcategories
where aTest.Id == value.Id
select aTest).Single();
}
}
I have a form with 2 combos, among other fields. 1 combo is filled with brand names (cmbMarca). This combo is filled correctly.
The other combo (cmbModelo) should be filled with models of the selected brand.
My problem is that the combo with models (cmbModelo) is not updated when selecting a brand (cmbBrand). When I select a brand, runs all the code but does not display any item in the combo "cmbModelo"
FillingForm.xaml
<Input:SfComboBox x:Name="cmbMarca" x:uid="BrandsCombo"
DisplayMemberPath="marca"
ItemsSource="{Binding MarcasSAT.Marcas}" SelectedValue="{Binding marca, Mode=TwoWay}"
SelectedValuePath="marca"
Tag="{Binding Path=SelectedMarca, Mode=TwoWay}" SelectionChanged="cmbMarca_SelectionChanged"/>
<Input:SfComboBox x:Name="cmbModelo" x:uid="ModelosCombo"
DisplayMemberPath="modelo"
ItemsSource="{Binding ModelosSAT.Modelos}"
SelectedValue="{Binding modelo, Mode=TwoWay}"
SelectedValuePath="modelo" />
FillingForm.CS
private void cmbMarca_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
MarcaViewModel curItem = (MarcaViewModel)cmbMarca.SelectedItem;
this.cmbMarca.Tag = curItem.idMarca;
}
FillingViewModel.cs
public class FillingViewModel : ViewModelBase
{
private readonly MarcasViewModel marcasSAT = new MarcasViewModel();
public MarcasViewModel MarcasSAT
{
get { return this.marcasSAT; }
}
private ModelosViewModel modelosSAT = new ModelosViewModel();
public ModelosViewModel ModelosSAT
{
get
{
return this.modelosSAT;
}
set
{
modelosSAT = value;
RaisePropertyChanged("ModelosSAT");
}
}
private int _selectedMarca;
public int SelectedMarca
{
get { return _selectedMarca; }
set
{
_selectedMarca = value;
RaisePropertyChanged("SelectedMarca");
modelosSAT.MarcaID = _selectedMarca;
}
}
}
ModelosViewModel.cs
public class ModelosViewModel : ViewModelBase
{
private ObservableCollection<ModeloViewModel> modelos;
public ObservableCollection<ModeloViewModel> Modelos
{
get
{
return modelos ;
}
set
{
modelos = value;
RaisePropertyChanged("Modelos");
}
}
public ObservableCollection<ModeloViewModel> GetModelos()
{
modelos = new ObservableCollection<ModeloViewModel>();
using (var db = new SQLite.SQLiteConnection(App.DBPath))
{
var query = db.Table<Modelos>().Where(c => c.idMarca == _marcaID);
//var query = db.Table<Modelos>();
foreach (var _mrc in query)
{
var mrc = new ModeloViewModel()
{
idMarca = _mrc.idMarca,
idModelo = _mrc.idModelo,
modelo = _mrc.modelo
};
modelos.Add(mrc);
}
}
return modelos ;
}
private int _marcaID=0;
public int MarcaID
{
get {
return _marcaID;
}
set
{
_marcaID = value;
RaisePropertyChanged("MarcaID");
this.GetModelos();
}
}
//public ModelosViewModel()
//{
// this.modelos = GetModelos();
//}
}
MarcasViewModel.cs
public class MarcasViewModel : ViewModelBase
{
private ObservableCollection<MarcaViewModel> marcas;
public ObservableCollection<MarcaViewModel> Marcas
{
get
{
return marcas ;
}
set
{
marcas = value;
RaisePropertyChanged("Marcas");
}
}
public ObservableCollection<MarcaViewModel> GetMarcas()
{
marcas = new ObservableCollection<MarcaViewModel>();
using (var db = new SQLite.SQLiteConnection(App.DBPath))
{
var query = db.Table<Marcas>();
foreach (var _mrc in query)
{
var mrc = new MarcaViewModel()
{
idMarca = _mrc.idMarca ,
marca = _mrc.marca
};
marcas.Add(mrc);
}
}
return marcas ;
}
public MarcasViewModel()
{
this.marcas = GetMarcas();
}
}
ViewModelBase.cs
public class ViewModelBase
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void RaisePropertyChanged(string propertyName)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
UPDATE1:
when I select a brand item, only the next code is called and in this order:
1st (in FillinfgForm.cs)
private void cmbMarca_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
MarcaViewModel curItem = (MarcaViewModel)cmbMarca.SelectedItem;
this.cmbMarca.Tag = curItem.idMarca;
}
2nd (in FillingViewModel.cs)
public int SelectedMarca
set
{
_selectedMarca = value;
RaisePropertyChanged("SelectedMarca");
modelosSAT.MarcaID = _selectedMarca;
}
3rd (in ModelosViewModel.cs)
Public int MarcaID
{
set
{
_marcaID = value;
RaisePropertyChanged("MarcaID");
this.GetModelos();
}
}
4th
public ObservableCollection<ModeloViewModel> GetModelos()
{
modelos = new ObservableCollection<ModeloViewModel>();
using (var db = new SQLite.SQLiteConnection(App.DBPath))
{
var query = db.Table<Modelos>().Where(c => c.idMarca == _marcaID);
//var query = db.Table<Modelos>();
foreach (var _mrc in query)
{
var mrc = new ModeloViewModel()
{
idMarca = _mrc.idMarca,
idModelo = _mrc.idModelo,
modelo = _mrc.modelo
};
modelos.Add(mrc);
}
}
return modelos ;
}
The problem is in GetMarcas() methods.
As you can see you work not with the property but with the field - 'marcas'. But when you assign a value to a field (in you particular case marcas = new ObservableCollection();) PropertyChanged method is not invoked. That is why you don't see UI changes.
You should work with the property istead of field or you can clear the existing ObservableCollection and add you values. Both variants should work.
So the following code should work:
public ObservableCollection<MarcaViewModel> GetMarcas()
{
Marcas = new ObservableCollection<MarcaViewModel>();
using (var db = new SQLite.SQLiteConnection(App.DBPath))
{
var query = db.Table<Marcas>();
foreach (var _mrc in query)
{
var mrc = new MarcaViewModel()
{
idMarca = _mrc.idMarca ,
marca = _mrc.marca
};
marcas.Add(mrc);
}
}
return marcas ;
}
or, a better solution:
public ObservableCollection<MarcaViewModel> GetMarcas()
{
Marcas.Clear();
using (var db = new SQLite.SQLiteConnection(App.DBPath))
{
var query = db.Table<Marcas>();
foreach (var _mrc in query)
{
var mrc = new MarcaViewModel()
{
idMarca = _mrc.idMarca ,
marca = _mrc.marca
};
Marcas.Add(mrc);
}
}
return marcas;
}
Also there is a good practice called "do not ignore return values", as you can see you don't need to return ObservableCollection from GetMarcas, so either rewrite it so that it returns nothing or rewrite invoking code (it dependes on your existing code).
I would like to know how to bind a custom data type to a TreeView.
The data type is basically an arraylist of objects that contain other arraylists. Access would look something like this:
foreach (DeviceGroup dg in system.deviceGroups)
{
foreach (DeviceType dt in dg.deviceTypes)
{
foreach (DeviceInstance di in dt.deviceInstances)
{
}
}
}
I would like the TreeView to look something like this:
DeviceGroup1
--> DeviceType1
--DeviceInstance1
--DeviceInstance2
--> DeviceType2
--DeviceInstance1
DeviceGroup2
--> DeviceType1
--DeviceInstance1
--> DeviceType2
Ok this is where the HierarchicalDataTemplate will save you. The trick is you will need to use two different hierarchical templates, since you have a three-level hierarchy here. I have constructed a simple UserControl to illustrate. First, here is some code-behind creating model data similar to what you have:
public partial class ThreeLevelTreeView : UserControl
{
public ArrayList DeviceGroups { get; private set; }
public ThreeLevelTreeView()
{
DeviceInstance inst1 = new DeviceInstance() { Name = "Instance1" };
DeviceInstance inst2 = new DeviceInstance() { Name = "Instance2" };
DeviceInstance inst3 = new DeviceInstance() { Name = "Instance3" };
DeviceInstance inst4 = new DeviceInstance() { Name = "Instance4" };
DeviceType type1 = new DeviceType() { Name = "Type1", DeviceInstances = new ArrayList() { inst1, inst2 } };
DeviceType type2 = new DeviceType() { Name = "Type2", DeviceInstances = new ArrayList() { inst3 } };
DeviceType type3 = new DeviceType() { Name = "Type3", DeviceInstances = new ArrayList() { inst4 } };
DeviceType type4 = new DeviceType() { Name = "Type4" };
DeviceGroup group1 = new DeviceGroup() { Name = "Group1", DeviceTypes = new ArrayList() { type1, type2 } };
DeviceGroup group2 = new DeviceGroup() { Name = "Group2", DeviceTypes = new ArrayList() { type3, type4 } };
DeviceGroups = new ArrayList() { group1, group2 };
InitializeComponent();
}
}
public class DeviceGroup
{
public string Name { get; set; }
public ArrayList DeviceTypes { get; set; }
}
public class DeviceType
{
public string Name { get; set; }
public ArrayList DeviceInstances { get; set; }
}
public class DeviceInstance
{
public string Name { get; set; }
}
Nothing difficult here, but note that you should use ObservableCollection instead of ArrayList if you want to add and remove from your collections dynamically. Now let's look at the XAML for this control:
<UserControl x:Class="TestWpfApplication.ThreeLevelTreeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestWpfApplication"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<TreeView ItemsSource="{Binding DeviceGroups}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding DeviceTypes}">
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding DeviceInstances}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
And here is the result:
alt text http://img684.imageshack.us/img684/6281/threeleveltreeview.png
I lately had to deal with a similar issue and after much research was able to get to a good generic solution. My problem was a bit more generic: Visualize a .NET object's properties in a tree view.
So given this class
```
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public List<Person> Children { get; set; }
}
I should see a tree like for an example instance:
- root
- FirstName: John
- LastName: Smith
- Children:
- [0]
- FirstName: Ann
- LastName: Smith
```
It's rather difficult to use reflection and go over an object's properties and such. Luckily, we already have a library that does that - Newtonsoft.Json.
My trick is to serialize the object with Newtonsoft.Json, then deserialize with System.Web.Script.Serialization.JavaScriptSerializer.
JavaScriptSerializer returns ArrayLists and Dictionaries, which are pretty simple to go over and add to the tree.
Thanks to this article which gave me some of the concept ideas.
Anyway, here's the entire code:
In XAML:
<TreeView ItemsSource="{Binding TreeItemsSource}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:TreeNode}" ItemsSource="{Binding Path=Children}">
<TreeViewItem>
<TreeViewItem.Header>
<StackPanel Orientation="Horizontal" Margin="-10,0,0,0">
<TextBlock Text="{Binding Path=Name}"/>
<TextBlock Text=" : "/>
<TextBox Text="{Binding Path=Value}" IsReadOnly="True"/>
</StackPanel>
</TreeViewItem.Header>
</TreeViewItem>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
In ViewModel:
public IEnumerable<TreeNode> TreeItemsSource
{
get
{
TreeNode tree = TreeNode.CreateTree(SelectedSession);
return new List<TreeNode>() { tree };
}
}
And the TreeNode class
public class TreeNode
{
public string Name { get; set; }
public string Value { get; set; }
public List<TreeNode> Children { get; set; } = new List<TreeNode>();
public static TreeNode CreateTree(object obj)
{
JavaScriptSerializer jss = new JavaScriptSerializer();
var serialized = Newtonsoft.Json.JsonConvert.SerializeObject(obj);
Dictionary<string, object> dic = jss.Deserialize<Dictionary<string, object>>(serialized);
var root = new TreeNode();
root.Name = "session";
BuildTree2(dic, root);
return root;
}
private static void BuildTree2(object item, TreeNode node)
{
if (item is KeyValuePair<string, object>)
{
KeyValuePair<string, object> kv = (KeyValuePair<string, object>)item;
TreeNode keyValueNode = new TreeNode();
keyValueNode.Name = kv.Key;
keyValueNode.Value = GetValueAsString(kv.Value);
node.Children.Add(keyValueNode);
BuildTree2(kv.Value, keyValueNode);
}
else if (item is ArrayList)
{
ArrayList list = (ArrayList)item;
int index = 0;
foreach (object value in list)
{
TreeNode arrayItem = new TreeNode();
arrayItem.Name = $"[{index}]";
arrayItem.Value = "";
node.Children.Add(arrayItem);
BuildTree2(value, arrayItem);
index++;
}
}
else if (item is Dictionary<string, object>)
{
Dictionary<string, object> dictionary = (Dictionary<string, object>)item;
foreach (KeyValuePair<string, object> d in dictionary)
{
BuildTree2(d, node);
}
}
}
private static string GetValueAsString(object value)
{
if (value == null)
return "null";
var type = value.GetType();
if (type.IsArray)
{
return "[]";
}
if (value is ArrayList)
{
var arr = value as ArrayList;
return $"[{arr.Count}]";
}
if (type.IsGenericType)
{
return "{}";
}
return value.ToString();
}
}