I'm new to WPF and trying to wrap my head around the preferred way to handle data. I found this link that explains the databinding for a tree view. I have tried to create my code in a similar way, but I can't see why that code runs fine and mine doesn't.
Anyway, I've defined some class for artists/albums/songs
class LibArtist
{
public string Name { get { return mName; } }
string mName;
public ObservableCollection<LibAlbum> Albums;
public LibArtist(string name)
{
mName = name;
Albums = new ObservableCollection<LibAlbum>();
}
}
class LibAlbum
{
public string Name { get { return mName; } }
public string Artist { get { return mArtist.Name; } }
public uint Year { get { return mYear; } }
public ObservableCollection<LibSong> mSongs = new ObservableCollection<LibSong>();
uint mYear;
LibArtist mArtist;
string mName;
public LibAlbum(string pName, LibArtist pArtist, uint pYear)
{
mName = pName;
mArtist = pArtist;
mYear = pYear;
}
}
class LibSong
{
public string Title { get { return mName; } }
public string Artist { get { return mArtist; } }
public string Album { get { return mAlbum; } }
public string Location { get { return mLocation; } }
public uint Year { get { return mYear; } }
string mName;
uint mYear;
string mAlbum;
string mArtist;
string mLocation;
public LibSong(string pSongLocation)
{
mLocation = pSongLocation;
TagLib.File lFile = TagLib.File.Create(pSongLocation);
mAlbum = lFile.Tag.Album;
mName = lFile.Tag.Title;
mArtist = lFile.Tag.AlbumArtists.Length > 0 ? lFile.Tag.AlbumArtists[0] : "???";
//use tag lib to fill the data if this file exists
mYear = lFile.Tag.Year;
}
public override bool Equals(object obj)
{
LibSong temp = obj as LibSong;
if (temp == null)
return false;
if (temp.Location == this.Location)
return true;
if (temp.Artist == this.Artist && temp.Album == this.Album && temp.Year == this.Year)
return true;
return false;
}
}
And these sit in a library class:
class Library
{
public SortedDictionary<string, List<string>> mArtistsToAlbums;
SortedDictionary<string, List<LibSong>> mAlbumsToSongs;
public List<LibSong> mSongList;
public ObservableCollection<LibSong> mSongList2;
public ObservableCollection<LibAlbum> mAlbumList;
public ObservableCollection<LibArtist> mArtistList;
...
}
In my main window, I set the data context of my treeview to the library object:
public MainWindow()
{
mPlayer = new izPlayer(0);
InitializeComponent();
libraryTreeView.DataContext = mLibrary;
mLibrary = new Library();
mLibrary.CreateTestData();
In my xaml, I define the treeview like so:
<TreeView Name="libraryTreeView"
HorizontalAlignment="Left"
ItemsSource="{Binding mArtistList}"
Height="443" Margin="10,50,0,0" VerticalAlignment="Top" Width="344" MouseDoubleClick="libraryTreeView_MouseDoubleClick"
>
<TreeView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
And when I run this, I don't get anything displayed in the treeview. As I said, I'm not sure why this is different from the example code, or why it isn't displaying the data inside mArtistList.
Any help would be appreciated!
Specifically for the TreeView Dennis' answer is a great resource. If you're not getting any items even in at the top level thought, it may be due to invalid binding sources. It looks like Library is declaring public fields
public ObservableCollection<LibArtist> mArtistList;
In order to use binding in the XAML these sources need to be public properties
public ObservableCollection<LibArtist> mArtistList { get; set; }
This is totally different from example code (I mean XAML difference).
The main concept for the data-bound TreeView in WPF is that you must describe hierarchical data templates for your nodes, because you want to display hierarchical data.
Your XAML should look like this:
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type yourNamespace:LibArtist}" ItemsSource="{Binding Albums}">
<!-- the template tree for displaying artist's data -->
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type yourNamespace:LibAlbum}" ItemsSource="{Binding Songs}">
<!-- the template tree for displaying song's data -->
</HierarchicalDataTemplate>
<!-- and so on -->
</TreeView.Resources>
Related
i want to display data in a CollectionView that has state = false using CommunityToolkit.Mvvm, but I don't quite understand how to do it. I wanted to use ICollectionView but didn't find it in maui. help me please
Model
public class Task
{
public string Title { get; set; }
public string Text { get; set; }
public bool State { get; set; }
public DateTime CreateDate { get; set; }
}
ViewModel
public partial class ToDoViewModel : ObservableObject
{
[ObservableProperty]
string title;
[ObservableProperty]
string text;
[ObservableProperty]
bool state = false;
[ObservableProperty]
DateTime createDate = DateTime.Now.Date;
[ObservableProperty]
ObservableCollection<Task> tasks;
int count = 1;
public ToDoViewModel()
{
tasks = new ObservableCollection<Task>();
}
[RelayCommand]
void Add()
{
if (string.IsNullOrEmpty(text))
return;
Task task = new Task
{
Title = $"Task #{count}",
Text = text,
State = state,
CreateDate = createDate
};
tasks.Add(task);
count++;
}
[RelayCommand]
void Remove(Task task)
{
if (tasks.Contains(task))
tasks.Remove(task);
}
[RelayCommand]
void StateDone(Task task)
{
task.State = true;
}
}
First, you need to set a CollectionView :
public ICollectionView TasksView { get; set; }
When you initiate your ViewModel, you may the source of CollectionView, eventually add some sorting. Then you need to update the CollectionView filter :
public ToDoViewModel()
{
tasks = new ObservableCollection<Task>();
TasksView = new CollectionViewSource { Source = tasks }.View;
TasksView.SortDescriptions.Add(new SortDescription("Title", ListSortDirection.Ascending));
this.updateTasksView();
}
private void updateTasksView()
{
if (ListStockBeamView != null)
{
TasksView.Filter = new Predicate<object>(getFilteredTasksView);
}
}
private bool getFilteredTasksView(object obj)
{
Task task = (Task)obj;
if (task.State==true) return false;//you could just write return !task.State
if(task.Title=="") return false // i.e you want to hide tasks without title
return true;
}
I always set the filter in a function, because in the future, if you want to add a CheckBox, to select state==true or state==false, or even filter the text title, or any other filter, it is easy to add additive filters, then launch the function everytime you edit your filters
I write a simple demo for you and CollectionView can be used in the MAUI.
I noticed that you wrote a model file and a ViewModel file. So I write the xaml file.
<CollectionView ItemsSource="{Binding tasks}" >
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding Title }" IsVisible="{Binding State}"></Label>
<Label Text="{Binding Text }"></Label>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
In the xaml, I bind the state to the IsVisible, so you can filter the data which you want to see.
Another way to use the state is by using the LINQ to select the data which the state is true
var TasksQuery =
from task in tasks
where task.State == "true"
select task;
Then you can use the TaskQuery which the state is all true.
Due to architecture design specifications, I have an application that fills its views from ClassLibraries. The application itself behaves like a sort of Integrator.
Now I need to add localization resources and I can successfully achieve it by adding *.resw files but only if the control is declared inside of the Application project.
What I actually need is to being able to share those resources across the ENTIRE SOLUTION somehow.
Then, the point is to being able to translate any control's content of the solution by using localization resources, preferably using the structure explained above.
For example, I have this following view, which fills the TextBlocks' content depending on the selected language:
<ComboBox x:Name="Languages"
ItemsSource="{Binding Languages}"
SelectedItem="{Binding SelectedLanguage, Mode=TwoWay}">
<i:Interaction.Behaviors>
<iCore:EventTriggerBehavior EventName="SelectionChanged">
<iCore:InvokeCommandAction Command="{Binding ChangeLanguage}" />
</iCore:EventTriggerBehavior>
</i:Interaction.Behaviors>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding LanguageName}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock Text="{Binding Model.HelloText}" FontSize="50" Foreground="Red"/>
<TextBlock Text="{Binding Model.HowAreYouText}" FontSize="50" Foreground="Red"/>
<BFview:BFView />
</StackPanel>
Where BFView is a view stored in another project (has two dummy textblocks also)
The Model of that view:
public class MainModel : TranslatableStrings
{
private string helloText, howareuText;
public string HelloText
{
get { return this.helloText; }
set { SetProperty(ref this.helloText, value); }
}
public string HowAreYouText
{
get { return this.howareuText; }
set { SetProperty(ref this.howareuText, value); }
}
}
And the base class of the Model is just a contractual class since it has no implementation, but a base type:
public abstract class TranslatableStrings : BindableBase { }
Then, the View data context is the following one:
public class MainViewModel : BaseViewModel
{
private ObservableCollection<MainViewListRscs> languages = new ObservableCollection<MainViewListRscs>();
private ICommand changeLang;
private MainModel model = new MainModel();
public MainViewModel()
{
Languages = new ObservableCollection<MainViewListRscs>()
{
new MainViewListRscs { LanguageCode = "es-ES", LanguageName = "Español" },
new MainViewListRscs { LanguageCode = "en-EN", LanguageName = "English" },
new MainViewListRscs { LanguageCode = "fr-FR", LanguageName = "Français" },
new MainViewListRscs { LanguageCode = "de-DE", LanguageName = "Deutsch" }
};
}
public ICommand ChangeLanguage
{
get { return changeLang = changeLang ?? new DelegateCommand(OnChangeLanguageRequested); }
}
public ObservableCollection<MainViewListRscs> Languages
{
get { return this.languages; }
set
{
this.languages = value;
OnPropertyChanged();
}
}
public MainViewListRscs SelectedLanguage { get; set; }
public MainModel Model
{
get { return this.model; }
set { this.model = value; }
}
private void OnChangeLanguageRequested()
{
Logger.Debug("MAINVIEW", SelectedLanguage.LanguageName + " selected.");
TranslateManager.UpdateStrings<TranslatableStrings>(SelectedLanguage.LanguageCode, this.Model);
}
public override Task OnNavigatedFrom(NavigationEventArgs args)
{
return null;
}
public override Task OnNavigatedTo(NavigationEventArgs args)
{
return null;
}
}
And the TranslateManager:
public class TranslateManager
{
public async static void UpdateStrings<T>(string langCode, T instance) where T : TranslatableStrings
{
//Get all the classes that implement TranslatableStrings
var currentAssembly = instance.GetType().GetTypeInfo().Assembly;
var translatableClasses = currentAssembly.DefinedTypes.Where(type => type.BaseType == typeof(T)).ToList();
//Open RESX file
ResourceLoader resx = ResourceLoader.GetForCurrentView(langCode);
foreach(var Class in translatableClasses)
{
foreach(var property in Class.DeclaredProperties)
{
string value = resx.GetString(property.Name);
var vmProp = instance.GetType().GetTypeInfo().GetDeclaredProperty(property.Name);
vmProp.SetValue(instance, value);
}
}
}
}
I have achieved changing the two TextBlocks of the MainView but not the view in another project. What I would need to do is to get a list of assemblies contained in a solution. I guess that getting just this would make everything work since I'm using a generic implementation.
Any suggestion will be much appreciated.
Thanks!
Your translation files are loaded as resources. So you can access them anywhere, even in other projects by doing something like
private ResourceLoader _resourceLoader = new ResourceLoader();
var someTranslation =_resourceLoader.GetString("your_localization_key");
Wrap this code nicely into a lib so that you can have an easy access to it from everywhere, and there you go !
i just started learning WPF as i am moving on from WinForm. At the moment i am having difficulties displaying bind data from class to tree view.
My tree view works perfectly if i use .Items.Add() method but when it comes to binding class data to TreeView this is what i see:
Here is the c# code:
public MainWindow()
{
InitializeComponent();
Search sc = new Search();
sc.query(null, "");
this.DataContext = sc;
}
Here is the xaml
<TreeView Width="400" Height="500" Name="TreeViewB" ItemsSource="{Binding getTreeResults}" Style="{StaticResource myTreeView}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Network}">
<TextBlock Text="{Binding getNetwork}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
Edited - 2 class added
Here is my class A
class Social_Searcher
{
List<Social_Network> networks = new List<Social_Network>();
public List<Social_Network> getTreeResults { get { return networks; } }
}
Here is my class B
class Social_Network
{
private string network_name;
private List<Keypair> data;
public Social_Network()
{
data = new List<Keypair>();
}
public struct Keypair
{
public void add(string _name, string _value)
{
name = _name;
value = _value;
}
public string name, value;
}
public string Network
{
get { return network_name; }
set { network_name = value; }
}
public void add(string name, string value)
{
if (name == "network")
{
network_name = value;
}
Keypair kp = new Keypair();
kp.add(name, value);
data.Add(kp);
}
public string getNetwork()
{
return network_name;
}
public List<Keypair> getData()
{
return data;
}
public string findKey_value(string key)
{
foreach (Keypair kp in data)
{
if (kp.name == key) return kp.value.ToString();
}
return "null";
}
}
You don't give much code, but getTreeResults and getNetwork look like methods, and your TextBlock will not know how to present them (normally, it would use the results of ToString(), but I don't know if that will work with a method.
If you want those methods, you can try it this way:
public string TreeResults { get { return sc.getTreeResuls(); }}
and then
<TreeView ... ItemsSource={Binding TreeResults} ... > ...
The same goes for getNetwork. I.e., you wrap each method in a public property.
If you don't want to do that, or can't, you can use an IValueConverter
There is clearly something going on in your UI, but it's hard to tell what exactly.
You will likely find a debugging tool such as Snoop useful, as it will allow you to click on items in your UI and see how they exist in the logical tree. You can modify their properties while the program is running to experiment and learn what you need to change in your source code.
I ran into this issue when converting a Windows Forms application to WPF. I know it sounds ridiculous, but make sure that your value is stored in the TreeViewItem's "Header" property, NOT the "Name" property. Once I did this, my list populated as expected.
I am trying to use the following code example from the Infragistics site and I'd like edits in the XamDataCards to be reflected in the XamDataGrid. However, my DataSource for the XamDataGrid is an ObservableCollection<Companies> in my ViewModel. How can I also bind to the card and relay updates back to my Companies object in the ViewModel?
<igDP:XamDataGrid x:Name="dgCompanies" Theme="IGTheme" DataSource="{Binding Companies}" SelectedDataItemsScope="RecordsOnly">
<igDP:XamDataGrid.FieldSettings>
<igDP:FieldSettings CellClickAction="SelectCell" AllowEdit="True"/>
</igDP:XamDataGrid.FieldSettings>
</igDP:XamDataGrid>
<igDP:XamDataCards x:Name="XamDataCards1"
Grid.Row="1"
DataSource="{Binding Path=SelectedDataItems, ElementName=dgCompanies}"
Theme="IGTheme">
Edit: Added ViewModel
public class CompanyMgmtViewModel : ViewModelBase
{
private ObservableCollection<Object> _Companies = null;
public ObservableCollection<Object> Companies
{
get { return _Companies; }
set
{
if (_Companies != value)
{
_Companies = value;
RaisePropertyChanged(GetPropertyName(() => Companies));
}
}
}
public CompanyMgmtViewModel()
{
this.LoadData();
}
public void LoadData()
{
ObservableCollection<Object> records = new ObservableCollection<Object>();
var results = from res in AODB.Context.TCompanies
select res;
foreach (var item in results)
if (item != null) records.Add(item);
Companies = records;
}
}
The Model/Context code is just EF Database First generated.
You would need to bind your XamDataGrid's SelectedDataItems property to a property of type object[] ie. SelectedCompanies in your ViewModel and bind to that for your XamDataCards' datasource.
The accepted answer in this thread has a sample that shows how to do this, albeit with a ListBox instead of XamDataCards:
http://www.infragistics.com/community/forums/t/89122.aspx
Just replace that ListBox with your XamDataCards control, it works and updates the XamDataGrid. The ViewModel in the example is contained in the MainWindow code-behind, so it is MVVM like you want.
more info:
http://help.infragistics.com/Help/Doc/WPF/2014.1/CLR4.0/html/xamDataGrid_Selected_Data_Items.html
IG's SelectedDataItems is an object[] :
http://help.infragistics.com/Help/Doc/WPF/2014.1/CLR4.0/html/InfragisticsWPF4.DataPresenter.v14.1~Infragistics.Windows.DataPresenter.DataPresenterBase~SelectedDataItems.html
I couldn't have gotten to this answer without Theodosius' and Ganesh's input - so thanks to them, they both had partial answers.
I first tried to bind the SelectedDataItems of the XamDataGrid to the XamDataCards by way of a property on the ViewModel as Theodosius suggested, but that wasn't enough. Thanks to Ganesh, I implemented INotifyPropertyChanged on my model objects, by inheriting from ObservableObject in MVVMLight (how did I not know the Model needed this?).
Below are the relevant pieces of code to make it work.
I also implemented PropertyChanged.Fody as documented here; that's where the TypedViewModelBase<T> and removal of RaisePropertyChanged() comes from.
I'm also creating my Model objects by using a LINQ/Automapper .Project().To<T>() call which can be found here.
Model
public class Company : ObservableObject
{
public Company() { }
public int id { get; set; }
public string strName { get; set; }
public string strDomicileCode { get; set; }
}
ViewModel
public class CompanyMgmtViewModel : TypedViewModelBase<Company>
{
private ObservableCollection<Object> _Companies = null;
private Object[] _selectedCompany = null;
public Object[] Company
{
get { return _selectedCompany; }
set
{
if (_Company != value)
{
_selectedCompany = value;
}
}
}
public ObservableCollection<Object> Companies
{
get { return _Companies; }
set
{
if (_Companies != value)
{
_Companies = value;
}
}
}
public CompanyMgmtViewModel()
{
this.LoadData();
}
public void LoadData()
{
ObservableCollection<Object> records = new ObservableCollection<Object>();
var results = AODB.Context.TCompanies.Project().To<Company>();
foreach (var item in results)
if (item != null) records.Add(item);
Companies = records;
}
}
View
<igDP:XamDataGrid x:Name="dgCompanies"
Theme="IGTheme"
DataSource="{Binding Companies, Mode=OneWay}"
SelectedDataItemsScope="RecordsOnly"
SelectedDataItems="{Binding Company}">
...
<igDP:XamDataCards x:Name="XamDataCards1"
Grid.Row="1"
DataSource="{Binding ElementName=dgCompanies, Path=SelectedDataItems}"
Theme="IGTheme">
Im struggling with Observable collections and using it to add pushpins onto a silverlight bing map. Im trying to build up a collection here using Linq. But im getting the error under every "PushPinItems" instance in my code saying:
'observable_collection_test.Map.PushPinItems' is a 'field' but is used like a 'type' c:\users\dan\documents\visual studio 2010\Projects\observable collection test\observable collection test\Map.xaml.cs 26 38 observable collection test
Not sure whats going on here, am I declaring/constructing it wrong or something?
Im new to Observable collections (and most of c#!) so any help/advice welcome. Many thanks.
UPDATE:
This seems to be ok now, the above issue, but now its not binding my items to pushpins.
I have looked at the "PushPins = pushPinCollection;" method and all 143 items are in there with lat, long and location propertiess with the correct data- as per this breakpoint:
Maybe there is an issue with my XAML binding?
Here is the updated code:
namespace observable_collection_test
{
public partial class Map : PhoneApplicationPage
{
private ObservableCollection<SItem2> _PushPins;
public event PropertyChangedEventHandler PropertyChanged;
public Map()
{
InitializeComponent();
getItems();
}
public ObservableCollection<SItem2> PushPins
{
get
{
return _PushPins;
}
private set
{
_PushPins = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("PushPins"));
}
}
}
private GeoCoordinate _location;
public GeoCoordinate Location
{
get { return _location; }
set
{
if (_location != value)
{
_location = value;
}
}
}
private string _pinSource;
public string PinSource
{
get { return _pinSource; }
set
{
if (_pinSource != value)
{
_pinSource = value;
}
}
}
public void getItems()
{
var document = XDocument.Load("ListSmall.xml");
if (document.Root == null)
return;
var xmlns = XNamespace.Get("http://www.blahblah.co.uk/blah");
var events = from ev in document.Descendants("item")
select new
{
Latitude = Convert.ToDouble(ev.Element(xmlns + "Point").Element(xmlns + "lat").Value),
Longitude = Convert.ToDouble(ev.Element(xmlns + "Point").Element(xmlns + "long").Value),
};
ObservableCollection<SItem2> pushPinCollection = new ObservableCollection<SItem2>();
foreach (var ev in events)
{
SItem2 PushPin = new SItem2
( ev.Latitude, ev.Longitude)
{
};
pushPinCollection.Add(PushPin);
}
PushPins = pushPinCollection;
}
other class:
namespace observable_collection_test
{
public class SItem2
{
//public DateTimeOffset Date { get; set; }
//public string Title
//{ get; set; }
public double Latitude
{ get; set; }
public double Longitude
{ get; set; }
public GeoCoordinate Location
{ get; set; }
//public Uri Link { get; set; }
public SItem2(//string Title,
double Latitude, double Longitude)
{
//this.Date = Date;
//this.Title = Title;
this.Latitude = Latitude;
this.Longitude = Longitude;
//this.Location = Location;
//this.Link = Link;
}
}
Bit of XAML concerning adding pins to map:
<my:Map ZoomBarVisibility="Visible" ZoomLevel="10" CredentialsProvider="AhqTWqHxryix_GnWER5WYH44tFuutXNEPvFm5H_CvsZHQ_U7-drCdRDvcWSNz6aT" Height="508" HorizontalAlignment="Left" Margin="0,22,0,0" Name="map1" VerticalAlignment="Top" Width="456">
<my:MapItemsControl ItemsSource="{Binding PushPins}" >
<my:MapItemsControl.ItemTemplate>
<DataTemplate>
<my:Pushpin Background="Aqua" Location="{Binding Location}" ManipulationCompleted="pin_click">
</my:Pushpin>
</DataTemplate>
</my:MapItemsControl.ItemTemplate>
</my:MapItemsControl>
</my:Map>
It would also be good to know if I am approaching the pushpin binding to the maps in the right way.
It looks as if this is because you have used x:Name="PushPinItems" in your XAML which is the same name as one of your types, so when you think you are referencing your PushPinItems type in your codebehind, you are actually referencing the field that VS has generated for you from your XAML that represents that Pushpin instance. You could use a different x:Name in your XAML.
Update
Ok, I see the issue :) I haven't worked with the Bing maps control before, but looking at http://forums.silverlight.net/forums/t/197631.aspx (second post down), you need to set the map controls MapItemsControl property. The ItemsSource property here should be bound to your ObservableCollection of a custom type which contains properties such as Name and Location. You can then populate this collection with instances of this custom type (in the post they have used MapData as the type name).
You can also get more examples and source code at http://www.microsoft.com/maps/isdk/silverlight/