Bind mui ModernTab wpf mvvm - wpf

im rookie with wpf + mvvm, have a simple mui:ModernTab control with items harcoded.
<mui:ModernTab Layout="List" SelectedSource="/Pages/Settings/Appearance.xaml">
<mui:ModernTab.Links>
<mui:Link DisplayName="appearance" Source="/Pages/Settings/Appearance.xaml" />
<mui:Link DisplayName="about" Source="/Pages/Settings/About.xaml" />
</mui:ModernTab.Links>
</mui:ModernTab>
I want populate it tab with the dbdata on the constructor of viewModel something like this on xaml code:
<ScrollViewer>
<mui:ModernTab Layout="List" Links="{Binding AllowedViews}" />
</ScrollViewer>
on viewModel c# constructor as:
public class ApplicationViewModel:ViewModelBase
{
private LinkCollection allowedViews;
public LinkCollection AllowedViews
{
get { return allowedViews; }
set {
allowedViews = value;
NotifyPropertyChanged("tabitem");
}
}
public ApplicationViewModel()
{
allowedViews.Add(new Link() { DisplayName = "item1"});
allowedViews.Add(new Link() { DisplayName = "item2" });
allowedViews.Add(new Link() { DisplayName = "item3" });
}
//allowedViews.Add(new Link() { DisplayName = "Otra Ventana", Source = new Uri("/Views/ModernWindow1.xaml", UriKind.RelativeOrAbsolute) });
}
Questions:
1-is better use a LinkCollection or List to populate data.
The right way to do the binding is with prop Links on xaml?
someone can sahre any documentation or example?
Thanks a lot. excuse my english.

public LinkCollection AllowedViews
{
get { return allowedViews; }
set {
allowedViews = value;
NotifyPropertyChanged("tabitem");
}
}
This "tabitem" should be "AllowedViews", right?

Here is a definition of dynamic links
<mui:ModernTab Layout="List" Links ="{Binding MyIEnumerable, Converter={StaticResource myCollectionToLinksConverter}}">
<mui:ModernTab.ContentLoader>
<app:MyControlLoader />
</mui:ModernTab.ContentLoader>
</mui:ModernTab>
then add a definition of a converter to your window or control
<UserControl.Resources>
<MyCollectionToLinksConverter x:Key="myCollectionToLinksConverter"/>
</UserControl.Resources>
then add the converter class
public class MyCollectionToLinksConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var source = (ICollection<MyCollectionItem>)value;
return new LinkCollection(source.Select(i => new Link() {DisplayName = i.Name, Source = new Uri(v.i, UriKind.Relative)}));
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
then add your content Loader
class MyControlLoader: DefaultContentLoader
{
protected override object LoadContent(Uri uri)
{
var myTarget = UIModel.Instance.GetMyTargetObjectById(v => v.Name == uri.OriginalString);
return new YourTabContentControl() {DataContext = myTarget};
}
}

Related

WPF Multi-Binding / Aggregate Binding to Collection

I have a Control that I want to automatically disappear if another control has no visibile children. I'm not sure how to implement that though. I feel as though I need to create a binding that returns bindings for each child element's visible property and then aggregates them into a MultiValueConverter. I think it is working but it seems as though when I add items to my collection, the collection binding isn't being re-evaluated. Has anyone done this before?
Below is my code:
<Grid.Resources>
<local:BindingExpander x:Key="BindingExpander"/>
<local:TestConverter x:Key="TestConverter" />
</Grid.Resources>
<Button Content="Button" HorizontalAlignment="Left" Margin="237,166,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click">
<Button.Visibility>
<MultiBinding Converter="{StaticResource TestConverter}">
<Binding ElementName="lstItems" Path="Items" Converter="{StaticResource BindingExpander}" ConverterParameter="Visibility"/>
</MultiBinding>
</Button.Visibility>
</Button>
<ListBox x:Name="lstItems" HorizontalAlignment="Left" Height="100" Margin="601,130,0,0" VerticalAlignment="Top" Width="100" DisplayMemberPath="Content"/>
and:
public class TestConverter : IMultiValueConverter {
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) {
var ret = Visibility.Collapsed;
foreach (var item in values) {
if(item is IEnumerable IE) {
foreach (var Child in IE) {
}
}
}
return ret;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}
public class BindingExpander : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
var ret = new List<Binding>();
if(value is IEnumerable IE) {
foreach (var item in IE) {
ret.Add(new Binding(parameter.ToString()) {
Source = item,
Mode = BindingMode.OneWay
});
}
}
return ret;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}
````
I have a Control that I want to automatically disappear if another
control has no visibile children..
Simply create a Boolean property which reports the status of what the other control is binding to such as:
public bool HasItems { get { return _SomeArray?.Any(); }}
This property can be as elaborate as needed, but a basic one above for the example is shown.
Then bind the visibility flag of the control in question to the HasItems.
Note that the HasItems does not have the plumbing for INotifyPropertyChanged. In the code(s) where items are added to the _SomeArray simply put in a call to PropertyChanged("HasItems")
On my blog I provide a basic example of that (Xaml: ViewModel Main Page Instantiation and Loading Strategy for Easier Binding) which looks like this where someone would bind to IsMemebershipAtMax such as what you are doing:
public bool IsMembershipAtMax
{
get { return MemberCount > 3; }
}
public int MemberCount
{
get { return _MemberCount; }
set
{
_MemberCount = value;
OnPropertyChanged();
OnPropertyChanged("IsMembershipAtMax");
}
}
public List<string> Members
{
get { return _Members; }
set { _Members = value; OnPropertyChanged(); }
}

Binding the Path Property of a Binding

is it possible to bind the Path property of a binding to another property?
I want to realize this code:
Text="{Binding Path={Binding Path=CurrentPath}}"
So I can adjust dynamically to which Property my actual binding is refering.
Thanks for your Help
Jonny
I worked it out on myself.
Heres the solution, I hope it might help anyone got the same problem like me.
public class CustomBindingBehavior : Behavior<FrameworkElement>
{
public bool IsBinding
{
get
{
return (bool)GetValue(IsBindingProperty);
}
set
{
SetValue(IsBindingProperty, value);
}
}
public string PropertyPath
{
get
{
return (string)GetValue(PropertyPathProperty);
}
set
{
SetValue(PropertyPathProperty, value);
}
}
public static DependencyProperty
PropertyPathProperty = DependencyProperty.Register("PropertyPath", typeof(string),
typeof(CustomBindingBehavior), null);
public static DependencyProperty
IsBindingProperty = DependencyProperty.Register("IsBinding", typeof(bool),
typeof(CustomBindingBehavior), null);
protected override void OnAttached()
{
if (AssociatedObject is TextBlock)
{
var tb = AssociatedObject as TextBlock;
tb.Loaded += new RoutedEventHandler(tb_Loaded);
}
}
private void tb_Loaded(object sender, RoutedEventArgs e)
{
AddBinding(sender as TextBlock, TextBlock.TextProperty);
}
private void AddBinding(DependencyObject targetObj, DependencyProperty targetProp)
{
if (IsBinding)
{
Binding binding = new Binding();
binding.Path = new PropertyPath(this.PropertyPath, null);
BindingOperations.SetBinding(targetObj, targetProp, binding);
}
else
{
targetObj.SetValue(targetProp, this.PropertyPath);
}
}
}
And heres the implementation in XAML:
<TextBlock >
<i:Interaction.Behaviors>
<behaviors:CustomBindingBehavior PropertyPath="{Binding Path=HeaderPropertyBinding}" IsBinding="{Binding Path=HeaderIsBinding}" />
</i:Interaction.Behaviors>
</TextBlock>
Greetings
Jonny
As other posters have mentioned, you can only set a binding on a dependency property - which path is not. The underlying reason is that xaml is source code that gets compiled. At compile time the compiler has no idea what the value of 'CurrentPath' is, and would not be able to compile. Essentially what you are looking to do is runtime reflection of a property value - which could be done using another property in the ViewModel you are binding to, or using a converter.
ViewModel:
public string CurrentValue
{
get
{
var property = this.GetType().GetProperty(CurrentPath);
return property.GetValue(this, null);
}
}
Using a converter:
public class CurrentPathToValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var viewModel = (ViewModel)value;
var property = viewModel.GetType().GetProperty(viewModel.CurrentPath);
var currentValue = property.GetValue(viewModel, null);
return currentValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Of couse these only work if you want to get a simple property of the object - if you want to get something more complex your reflection code is going to get a lot more complex.
Unless you are building something like a property grid, or for some other reason you actually want to introspect the objects running in your application, I would suggest you revisit your design, as reflection is really only suited to a few situations.
Path is not a dependency property, therefore the binding will not work.
Perhaps you could bind to a property that returns another property based on a switch statement and bind to that. Change the 'switch' property and you change the output of the other property.
Just don't forget to include your NotifyPropertyChanged stuff in the switch property for the bound property otherwise your view will not update.
e.g.
private int _mySwitch;
//Set this to determine what the other property will return.
public int SwitchProperty
{
get { return _mySwitch; }
set
{
_mySwitch = value;
NotifyPropertyChanged("MySwitchableProperty");
}
}
public String PropertyA { get; set; }
public String PropertyB { get; set; }
//Bind to this property
public String MySwitchableProperty
{
get
{
switch (SwitchProperty)
{
case 1:
return PropertyA;
break;
case 2:
return PropertyB;
break;
default :
return String.Empty;
break;
}
}
}
I think converter can helps your.
Expample
First control
Text="{Binding Path=CurrentPath}"
Second control
Text="{Binding Path=CurrentPath, Convertor={converters:MyConvertor}}"
Base converter
public abstract class ConvertorBase<T> : MarkupExtension, IValueConverter
where T : class, new()
{
public abstract object Convert(object value, Type targetType, object parameter,
CultureInfo culture);
public virtual object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
#region MarkupExtension members
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (_converter == null)
_converter = new T();
return _converter;
}
private static T _converter = null;
#endregion
}
MyConverter
public class MyConverter: ConvertorBase<MyConverter>
{
public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (string)value.Equals("blabla") ? "Yes" : "No"; // here return necessary parametr
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}

DependencyProperty not firing

I have the following XAML that uses a classes CurrencyImg property to look up a static resource at runtime, as there are a lot of these in a treeview, I don't want to have to load the same image 1000 times, I keep it in a resourcedictionary so it is only loaded once.
<Image Name="imgCurrency" Grid.Column="5" Margin="0,0,0,0" Source="{w:ImageStaticResource {Binding CurrencyImg}}" Height="22" VerticalAlignment="Top"/>
With a sample resource dictionary entry as
<ImageSource x:Key="..\Resources\Images\USD.ico">../Resources/Images/USD.ico</ImageSource>
The property is as follows
public string CurrencyImg
{
get
{
if (DisplayUSDRate)
{
return AppString.General.ImagePath + AppString.CurrencyId.USD + ".ico";
}
else
{
return AppString.General.ImagePath + curr + ".ico";
}
}
}
And the property action which does the work
public override bool DisplayUSDRate
{
get { return _customer.DisplayUSDRate; }
set
{
_customer.DisplayUSDRate = value;
OnPropertyChanged("CurrencyImg");
}
}
Now when this runs, the OnPropertyChanged("CurrencyImg") does nothing, and it's clear why. In the XAML I'm not directly binding to the CurrencyImg property, I'm using it as a parameter to my StaticResourceExtension class, so when I call OnPropertyChanged, it thinks there are no bound properties to update, so the XAML image does not update. I can understand this, but obviously this does not help me as what I need to happen is the following.
1) For the class to determine which currency image gets displayed for that row
2) For the images to come from a resource dictionary so they don't get loaded more than once or performance hugely suffers
3) For changes to the DisplayUSDRate property to then reflect by updating the flag to the appropriate image. This is the bit I can't figure out how to do with my StaticResourceExtension (code below)
using System;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
namespace Citi.Rbcs.UI.Windows
{
public class ImageStaticResource : StaticResourceExtension
{
public Binding Binding { get; set; }
private static readonly DependencyProperty DummyProperty;
public ImageStaticResource()
{
}
public ImageStaticResource(Binding binding)
{
Binding = binding;
}
static ImageStaticResource()
{
DummyProperty = DependencyProperty.RegisterAttached(
"Dummy",
typeof (Object),
typeof (DependencyObject),
new UIPropertyMetadata(null));
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
var target = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
var targetObject = (FrameworkElement)target.TargetObject;
Binding.Source = targetObject.DataContext;
var DummyDO = new DependencyObject();
BindingOperations.SetBinding(DummyDO, DummyProperty, Binding);
ResourceKey = DummyDO.GetValue(DummyProperty);
var resourceDictionary = new ResourceDictionary
{
Source = new Uri("pack://application:,,,/Windows/Images.xaml")
};
var key = (string) ResourceKey;
if (!resourceDictionary.Contains(key)) ResourceKey = "Default";
return base.ProvideValue(serviceProvider);
}
}
}
You could write a very simple binding converter that converts image URI strings to cached ImageSource objects:
public class StringToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var imageUrl = value as string;
var image = MemoryCache.Default.Get(imageUrl) as ImageSource;
if (image == null)
{
image = new BitmapImage(new Uri(imageUrl));
MemoryCache.Default.Set(imageUrl, image, new CacheItemPolicy());
}
return image;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

How can you get a XAML TextBlock in WP7 Silverlight to collapse when it contains no data?

I have a textblock inside a list view that I need to hide or collapse when it is empty or null. I tried using a string converter but that does not do it.
Any other ideas?
Update # 1:
Here is the code inside the textblock:
Visibility="{Binding Converter={StaticResource StringConverter}}
Here is the converter:
public class StringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
return string.IsNullOrEmpty(value.ToString()) ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}
I would recommend creating text and visibility bindings on your textbox.
Here's an example of the view model properties you'd have.
public String TextBoxText
{
get { return textBoxText; }
set
{
if (value != textBoxText)
{
textBoxText= value;
SetTextBoxVisibility();
OnPropertyChanged("TextBoxText");
}
}
}
private String textBoxText;
public Visibility TextBoxVisibility
{
get { return textBoxVisibility; }
set
{
if (value != textBoxVisibility)
{
textBoxVisibility= value;
OnPropertyChanged("TextBoxVisibility");
}
}
}
private Visibility textBoxVisibility;
public void SetTextBoxVisibility()
{
this.TextBoxVisibility = String.IsNullOrEmpty(this.TextBoxText) ? Visibility.Collapsed : Visibility.Visible;
}
The only thing you've not shown of your code is where you instantiate the converter class. Is this because you're not doing so?
Typically you'd add something like this to app.xaml:
<Application.Resources>
<ResourceDictionary>
<conv:StringConverter x:Key="StringConverter " />
</ResourceDictionary>
</Application.Resources>

Bind a Silverlight TabControl to a Collection

I have a Collection of Model-objects in my ViewModel. I would like to be able to bind a TabControl to these and use a DataTemplate to extract the information from the Model-objects. When I try to do this I get the errormessage: Unable to cast object of type Model to object of type TabItem. After spending some time looking for a solution I found the following:
The Silverlight TabControl is
broken. Use a combination of ListBox
and ContentControl to mimic the
behaviour of a TabControl. (Means
that I have to skin the ListBox to
look like a TabControl)
TabControl does not override
PrepareContainerForItemOverride and
the solution is to make a
Converter. (Not so good because I
then need to specify the type of the
convertee in the Converter)
Anyone know any better solution?
XAML
<sdk:TabControl ItemsSource="{Binding Items, ElementName=MyControl}">
<sdk:TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</sdk:TabControl.ItemTemplate>
</sdk:TabControl>
C#
public ObservableCollection<Model> Items { get; set; }
public ViewModel()
Items = new ObservableCollection<Model>{
new Model { Name = "1"},
new Model { Name = "2"},
new Model { Name = "3"},
new Model { Name = "4"}
};
}
Suggested Converter:
public class TabConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
List<TabSource> source = value as List<TabSource>;
if (source != null)
{
List<TabItem> result = new List<TabItem>();
foreach (TabSource tab in source)
{
result.Add(new TabItem()
{
Header = tab.Header,
Content = tab.Content
});
}
return result;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Create converter
public class SourceToTabItemsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
try
{
var source = (IEnumerable)value;
if (source != null)
{
var controlTemplate = (ControlTemplate)parameter;
var tabItems = new List<TabItem>();
foreach (object item in source)
{
PropertyInfo[] propertyInfos = item.GetType().GetProperties();
//тут мы выбираем, то поле которое будет Header. Вы должны сами вводить это значение.
var propertyInfo = propertyInfos.First(x => x.Name == "name");
string headerText = null;
if (propertyInfo != null)
{
object propValue = propertyInfo.GetValue(item, null);
headerText = (propValue ?? string.Empty).ToString();
}
var tabItem = new TabItem
{
DataContext = item,
Header = headerText,
Content =
controlTemplate == null
? item
: new ContentControl { Template = controlTemplate }
};
tabItems.Add(tabItem);
}
return tabItems;
}
return null;
}
catch (Exception)
{
return null;
}
}
/// <summary>
/// ConvertBack method is not supported
/// </summary>
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException("ConvertBack method is not supported");
}
Create ControlTemplate:
<ControlTemplate x:Key="MyTabItemContentTemplate">
<StackPanel>
<TextBlock Text="{Binding Path=name}" />
</StackPanel>
</ControlTemplate>
And binding convert, controltemplate
<controls:TabControl x:Name="tabControl"
ItemsSource="{Binding ElementName=tabControl,
Path=DataContext,
Converter={StaticResource ConverterCollectionToTabItems},
ConverterParameter={StaticResource MyTabItemContentTemplate}}">
</controls:TabControl>
taken from the blog binding-tabcontrol

Resources