Silverlight Combobox in DataTemplate - c#

I have defined a data template for a c# class in xaml as follows
<DataTemplate x:Key="ApplicationTemplate">
<StackPanel Orientation="Vertical">
<telerik:RadComboBox DisplayMemberPath="Name"
ItemsSource="{Binding CurrentItem.Apps, RelativeSource={RelativeSource FindAncestor, AncestorType=telerik:RadDataForm}}"
IsEnabled="{Binding IsReadOnly, RelativeSource={RelativeSource AncestorType=ContentControl}, Converter={StaticResource BooleanInverterConverter}}" />
</StackPanel>
</DataTemplate>
My "Apps" Enumerable is composed of this object:
public class InteractiveApplicationModel : ValidatingModel
{
public string Name
{
get { return GetProperty(() => Name); }
set { SetProperty(() => Name, value); }
}
public string Type
{
get { return GetProperty(() => Type); }
set { SetProperty(() => Type, value); }
}
public string URL
{
get { return GetProperty(() => URL); }
set { SetProperty(() => URL, value); }
}
public string Image
{
get { return GetProperty(() => Image); }
set { SetProperty(() => Image, value); }
}
public InteractiveApplicationModel()
{
this.Type = string.Empty;
this.Name = string.Empty;
this.URL = string.Empty;
this.Image = string.Empty;
}
public InteractiveApplicationModel(string name, string type, string url, string image)
{
this.Name = name;
this.Type = type;
this.URL = url;
this.Image = image;
}
}
}
In the View model i have:
public IList<InteractiveApplicationModel> Apps
{
get
{
return new List<InteractiveApplicationModel>()
{
new InteractiveApplicationModel(null,null,null,null),
new InteractiveApplicationModel("name","type","url","image"),
new InteractiveApplicationModel("name2","type2","url2","image2")
};
}
}
I then have a form page which loads "complex" objects which contain "InteractiveApplicationModel" objects and use the data template to display those objects.
Everything is working except one thing. I need the selected value of the combobox to be the same as the value of the "InteractiveApplicationModel" objects in my "complex" objects.
My "complex" objects can have up to 5 "InteractiveApplicationModel" objects inside them.

You have to Bind the SelectedItem property on the RadComboBox
SelectedItem="{Binding SelectedInteractiveApplicationModel, [.... ancestor ....] Mode=TwoWay}"
with a Selected property on the ViewModel
public InteractiveApplicationModel SelectedInteractiveApplicationModel
{
get { return GetProperty(() => SelectedInteractiveApplicationModel); }
set { SetProperty(() => SelectedInteractiveApplicationModel, value); }
}

Related

Add localization resources to be shared across multiple projects

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 !

Implementing combobox with command search

well i m having a problem when i use the method search i get only the textbox and not the combobox i m using mmvm here is my code:
in my constructor i have :
CountryList = new FastObservableCollection<Country>(DummyWebservice.GetCountries());
SearchCitizenCommand = new RelayCommand(SearchCitizen);
and for display countries and cities:
private FastObservableCollection<City> citylist;
public FastObservableCollection<City> CityList
{
get
{
return citylist;
}
set
{
Set(() => CityList, ref citylist, value);
}
}
private FastObservableCollection<Country> countryList;
public FastObservableCollection<Country> CountryList
{
get
{
return countryList;
}
set
{
Set(() => CountryList, ref countryList, value);
}
}
private Country selectedcountry;
public Country SelectedCountry
{
get
{
return selectedcountry;
}
set
{
Set(() => SelectedCountry, ref selectedcountry, value);
OnPropertyChanged(() => SelectedCity);
CityList = DummyWebservice.GetCitiesByCountryId(SelectedCountry.Id);
}
}
private City selectedcity;
public City SelectedCity
{
get
{
return selectedcity;
}
set
{
Set(() => SelectedCity, ref selectedcity, value);
}
}
and finnally in the method search i tried
SelectedCountry = new Country();
SelectedCountry.Name = citizen.Citizenship.Name;
in the view i got :
<ComboBox x:Name="txtBirthCountryPicker"
Grid.Row="1"
Grid.Column="1"
HorizontalContentAlignment="Left"
DisplayMemberPath="Name"
ItemsSource="{Binding CountryList}"
SelectedItem="{Binding SelectedCountry}" />
but im still getting it umpty
If you want to select an item in a combo, you need to choose the exact same object that is part of the ItemsSource.
SelectedCountry = CountryList.FirstOrDefault(
x => x.Name == citizen.Citizenship.Name
)
The Combobox uses object equality to determine which of item in the ItemsSource is the selected item. As you're assigning a new instance of Country to the SelectedItem, it is unable to locate the country in the ItemsSource.
Try this instead:
SelectedCountry = CountryList.FirstOrDefault(c => c.Name.Equals(citizen.Citizenship.Name, StringComparison.CurrentCultureIgnoreCase);

DevExpress - MVVM - Generate TabItems with different ViewModels

i have a DXTabControl. The DXTabItems are generated via my ViewModel.
//MainViewModel
public MainViewModel()
{
var items = new ObservableCollection<DXTabItem>();
items.Add(
new DXTabItem()
{
Header = "Test1",
Content = new WebViewModel()
});
items.Add(
new DXTabItem()
{
Header = "Test2",
Content = new CMSViewModel()
});
TabItems = items;
}
private ObservableCollection<DXTabItem> _tabItems;
public ObservableCollection<DXTabItem> TabItems
{
get { return _tabItems; }
set { SetProperty(ref _tabItems, value, () => TabItems); }
}
I am working with a DataTemplate and my TabItem is still not showing any UserControl.
//MainView.xaml
<DataTemplate x:Key="WebTemplate" DataType="{x:Type viewmodel:WebViewModel}">
<view:WebView/>
</DataTemplate>
<DataTemplate x:Key="CMSTemplate" DataType="{x:Type viewmodel:CMSViewModel}">
<view:CMSView/>
</DataTemplate>
<datatemplate:TemplateSelector x:Key="DataTemplateSelector"
WebTemplate="{StaticResource WebTemplate}"
CMSTemplate="{StaticResource CMSTemplate}" />
<dx:DXTabControl ItemsSource="{Binding TabItems}" ItemTemplateSelector="{StaticResource DataTemplateSelector}" />
//DataTemplateSelector
public class TemplateSelector : DataTemplateSelector
{
public DataTemplate WebTemplate { get; set; }
public DataTemplate CMSTemplate { get; set; }
public override DataTemplate SelectTemplate(Object item,
DependencyObject container)
{
if (item == null) return base.SelectTemplate(item, container);
if (item.GetType() == typeof(WebViewModel))
{
return WebTemplate;
}
else if (item.GetType() == typeof(CMSViewModel))
{
return CMSTemplate;
}
else return base.SelectTemplate(item, container);
}
}
Everything is working, except showing the content i need. No view is been shown. Any idea? Did i miss something?
The following answer is based on caliburn.micro.
Step 1: Add a convention to the bootstrapper
public Bootstrapper()
{
ConventionManager.AddElementConvention<DXTabControl>(DXTabControl.ItemsSourceProperty, "ItemsSource", "DataContextChanged")
.ApplyBinding = (viewModelType, path, property, element, convention) =>
{
if (!ConventionManager.SetBindingWithoutBindingOrValueOverwrite(viewModelType, path, property, element, convention, DXTabControl.ItemsSourceProperty))
{
return false;
}
var tabControl = (DXTabControl)element;
if (tabControl.ItemTemplate == null && tabControl.ItemTemplateSelector == null && property.PropertyType.IsGenericType)
{
var itemType = property.PropertyType.GetGenericArguments().First();
if (!itemType.IsValueType && !typeof(string).IsAssignableFrom(itemType))
{
tabControl.ItemTemplate = ConventionManager.DefaultItemTemplate;
}
}
ConventionManager.ConfigureSelectedItem(element, Selector.SelectedItemProperty, viewModelType, path);
if (string.IsNullOrEmpty(tabControl.DisplayMemberPath))
{
ConventionManager.ApplyHeaderTemplate(tabControl, DXTabControl.ItemHeaderTemplateProperty, DXTabControl.ItemHeaderTemplateSelectorProperty, viewModelType);
}
return true;
};
[...]
}
Now you can bind any Screen-Collection to your DXTabControl.
Step 2: Create a collection in the ViewModel
public class MainViewModel : Screen
{
public MainViewModel()
{
DisplayName = "DevExpress Test Environment";
}
private static BindableCollection<Screen> _tbCtrl = new BindableCollection<Screen>();
public BindableCollection<Screen> TbCtrl
{
get { return _tbCtrl; }
set
{
_tbCtrl = value;
NotifyOfPropertyChange(() => TbCtrl);
}
}
}
You can e.g. put any other ViewModel which is based on the Screen class to your collection. That means, you will be able to display your content for each tabitem.
Step 3: Create the DXTabControl in your View (XAML-Code)
<dx:DXTabControl x:Name="TbCtrl" />
Give it a go. Open for feedback.
/// Alternative solution without Caliburn.Micro
Step 1: Add the DXTabControl to your MainView (XAML-Code)
<dx:DXTabControl ItemsSource="{Binding TbCtrlItems}" />
Step 2: Your MainViewModel needs to add those items like i have described above (in my question), but in this case, you have to specify the content-property
public MainViewModel()
{
_tbCtrlItems.Add(new DXTabItem()
{
Header = "Test1",
Content = new Views.View1() {DataContext = new ViewModel1()}
});
_tbCtrlItems.Add(new DXTabItem()
{
Header = "Test2",
Content = new Views.View2() { DataContext = new ViewModel2() }
});
}
private ObservableCollection<DXTabItem> _tbCtrlItems = new ObservableCollection<DXTabItem>();
public ObservableCollection<DXTabItem> TbCtrlItems
{
get { return _tbCtrlItems; }
set { SetProperty(ref _tbCtrlItems, value, () => TbCtrlItems); }
}
I hope this answer is helpful.

WPF can't populate treeview

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>

wpf how to bind to List

i'm trying to bound a list of object to DataGrid but i'm getting wrong value:
the object class:
public class Attribute
{
public Attribute()
{
}
private string _name;
public string name
{
get { return _name; }
set { _name = value; }
}
private List<Value> _valueList = new List<Value>();
public List<Value> ValueList
{
get { return _valueList; }
set { _valueList = value; }
}
}
public class Value
{
private string _value;
public string value
{
get { return _value; }
set { _value = value; }
}
public override string ToString()
{
return _value.ToString();
}
}
and i'm having a list of objects: List<Attribute> attributes
attributeDataGrid.ItemsSource = attributes;
when i bound i get a grid with name column correct
but the "ValueList" shown as "(Collection)" instead of the string...
how should i bound the List ?
Your overriden ToString method in Value is not called, because WPF displays the content of ValueList in your second column, i.e. it displays ValueList.ToString().
What do you expect to see in the second column? A comma separated list of the values in the ValueList?
Try below things it works for me.
On Load Method write below code
Attribute atr = new Attribute();
atr.ValueList.Add(new Value() { value = "One" });
atr.ValueList.Add(new Value() { value = "Two" });
atr.ValueList.Add(new Value() { value = "Three" });
atr.ValueList.Add(new Value() { value = "Four" });
dataGrid.DataContext = atr.ValueList;
On XAML file try below
<DataGrid x:Name="dataGrid" AutoGenerateColumns="False" ItemsSource="{Binding}" >
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding value}" />
</DataGrid.Columns>
</DataGrid>
Hope this code snippet helps to you.

Categories

Resources