I want to change the background color of listview alternate rows. I am binding values to listview through ObservableCollection. So that I can't iterate through listview items. It shows:
`System.InvalidCastException: 'Unable to cast object of type 'xx.StudentClass' to type 'Windows.UI.Xaml.Controls.ListViewItem'.'
ObservableCollection<StudentClass> StudentData = new ObservableCollection<StudentClass>();
var statement = connection.Prepare("SELECT name,ID from student_details");
while (!(SQLiteResult.DONE == statement.Step()))
{
if (statement[0] != null)
{
StudentClass c1 = new StudentClass() { studentName= statement[0].ToString, studentID= statement[1].ToString};
StudentData.Add(c1);
}
}
StudentListview.ItemsSource = StudentData;
ChangeBgColor();
private void ChangeBgColor()
{
int counter = 1;
foreach (ListViewItem item in this.StudentListview.Items)
{
if (counter % 2 == 0)
{
item.Background = new SolidColorBrush(Colors.Orange);
}
else
{
item.Background = new SolidColorBrush(Colors.OrangeRed);
}
counter++;
}
}
<ListView x:Name="StudentListview" Visibility="Collapsed" VerticalAlignment="Top" HorizontalAlignment="Right" Height="250px" Width="550px">
<ListView.ItemTemplate >
<DataTemplate>
<Grid>
<StackPanel Orientation="Vertical" >
<StackPanel Orientation="Horizontal">
<TextBlock Foreground="Black" Text="{Binding studentName}" FontSize="20" Width="350px" TextWrapping="Wrap"></TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Foreground="Black" Text="{Binding studentID}" FontSize="20" Width="350px" TextWrapping="Wrap" ></TextBlock>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
If you want this to tackle in a future proof way, I would suggest creating a new ListView control that can handle this...
Here is how I did this, first define the new control with following properties
public class AlternatingListView : ListView
{
public static readonly DependencyProperty OddRowBackgroundProperty = DependencyProperty.Register(
nameof(OddRowBackground),
typeof(Brush),
typeof(AlternatingListView),
new PropertyMetadata(null));
public static readonly DependencyProperty EvenRowBackgroundProperty = DependencyProperty.Register(
nameof(EvenRowBackground),
typeof(Brush),
typeof(AlternatingListView),
new PropertyMetadata(null));
public Brush OddRowBackground
{
get { return (Brush)GetValue(OddRowBackgroundProperty); }
set { SetValue(OddRowBackgroundProperty, (Brush)value); }
}
public Brush EvenRowBackground
{
get { return (Brush)GetValue(EvenRowBackgroundProperty); }
set { SetValue(EvenRowBackgroundProperty, (Brush)value); }
}
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
ListViewItem listViewItem = element as ListViewItem;
if (listViewItem == null)
{
return;
}
int index = IndexFromContainer(element);
listViewItem.Background = (index + 1) % 2 == 1 ? OddRowBackground : EvenRowBackground;
}
}
With all this in place you can add that control your XAML and define the needed colors.
<controls:AlternatingListView x:Name="ListView"
ItemsSource="{x:Bind Items}"
EvenRowBackground="SlateGray"
OddRowBackground="White" />
I'm trying to bind a reusable button in a carusel, what I want to achieve is a add lets say 6 buttons each button will have a command that according to button name will navigate to the proper page.
I can do that by doing this:
<toolkitcontrols:Carousel x:Name="NavigationMenuCarouselPanel"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal"
ItemsSource="{x:Bind ViewModel.MenuList, Mode=OneWay}"
ItemMargin="25"
ItemDepth="160"
ItemRotationX="180"
ItemRotationY="25"
ItemRotationZ="0"
SelectedIndex="2"
Grid.Row="1">
<toolkitcontrols:Carousel.EasingFunction>
<CubicEase EasingMode="EaseOut"/>
</toolkitcontrols:Carousel.EasingFunction>
<Button Command="{x:Bind ViewModel.NavigateToPage1, Mode=OneWay}"
Content="{x:Bind ViewModel.Name, Mode=OneWay}"/>
</toolkitcontrols:Carousel>
If I do that, I'll be adding 5 more buttons and i'll have to write properties for each button.
So instead I want to use a UserControl and just write something like this:
<toolkitcontrols:Carousel x:Name="NavigationMenuCarouselPanel"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal"
ItemsSource="{x:Bind ViewModel.MenuList, Mode=OneWay}"
ItemMargin="25"
ItemDepth="160"
ItemRotationX="180"
ItemRotationY="25"
ItemRotationZ="0"
SelectedIndex="2"
Grid.Row="1">
<toolkitcontrols:Carousel.EasingFunction>
<CubicEase EasingMode="EaseOut"/>
</toolkitcontrols:Carousel.EasingFunction>
<toolkitcontrols:Carousel.ItemTemplate>
<DataTemplate x:DataType="data:ButtonInfo">
<usercontrolvm:NavigationMenuButtonTemplate NavigateToPageCommand="{Binding NavigateToPageCommand}"/>
</DataTemplate>
</toolkitcontrols:Carousel.ItemTemplate>
</toolkitcontrols:Carousel>
But I've failed doing it, I found out some tutorial but all as I understand will make me write this like of code:
<usercontrolvm:NavigationMenuButtonTemplate NavigateToPageCommand="{Binding NavigateToPageCommand}"/>
like 6 times, and i dont know how it will take x:DataType of DataTemplate for my list of properties.
This is my UserControl.xaml.cs
public sealed partial class NavigationMenuButtonTemplate : UserControl
{
public ButtonInfo ButtonInfo => (DataContext as ButtonInfo);
public NavigationMenuButtonTemplate()
{
this.InitializeComponent();
Loaded += NavigationMenuButtonTemplate_Loaded;
}
private void NavigationMenuButtonTemplate_Loaded(object sender, RoutedEventArgs e)
{
Bindings.Update();
}
public DelegateCommand NavigateToPageCommand
{
get { return (DelegateCommand)GetValue(NavigateToPageCommandProperty); }
set { SetValue(NavigateToPageCommandProperty, value); }
}
// Using a DependencyProperty as the backing store for NavigateToPageCommand. This enables animation, styling, binding, etc...
public static readonly DependencyProperty NavigateToPageCommandProperty =
DependencyProperty.Register("NavigateToPageCommand",
typeof(DelegateCommand),
typeof(NavigationMenuButtonTemplate),
new PropertyMetadata(0));
}
this is my ButtonInfo.cs
public class ButtonInfo
{
public string Symbol { get; set; }
public string FontFamily { get; set; }
public string MenuName { get; set; }
public string BenefitKind { get; set; }
public string Status { get; set; }
}
and this is my UserControl.xaml
<Button x:Name="NavigationMenuTemplate"
Width="300"
Height="300"
Command="{Binding NavigateToPageCommand, ElementName=root, Mode=OneWay}">
<Grid x:Name="ButtonLayout">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="NavigationMenuIconTextBlock"
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2"
FontFamily="{x:Bind ButtonInfo.FontFamily, Mode=OneWay, FallbackValue='Webdings'}"
Text="{x:Bind ButtonInfo.Symbol, Mode=OneWay, FallbackValue=''}"
FontSize="150"
Foreground="Black"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
<TextBlock x:Name="NavigationMenuButtonNameTextBlock"
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
Text="{x:Bind ButtonInfo.MenuName, Mode=OneWay, FallbackValue='CALCULADORA JORNADAS EXTRAORDINARIAS'}"
FontSize="12"
Foreground="Black"
HorizontalAlignment="Center"/>
<TextBlock x:Name="NavigationMenuButtonBenefitKindTextBlock"
Grid.Row="2"
Grid.Column="0"
Text="{x:Bind ButtonInfo.BenefitKind, Mode=OneWay, FallbackValue='Subscripción'}"
FontSize="10"
Foreground="Black"
HorizontalAlignment="Left"/>
<TextBlock x:Name="NavigationMenuButtonStatusTextBlock"
Grid.Row="2"
Grid.Column="1"
Text="{x:Bind ButtonInfo.Status, Mode=OneWay, FallbackValue='Vigente'}"
FontSize="10"
Foreground="Black"
HorizontalAlignment="Right"/>
</Grid>
</Button>
can somebody help me and point me in the right direction please.
what am I missing?
The ItemTemplate approach in your question is actually on the right track.
In the end, your XAML would look something similar to the following(only a few properties are included but you get the idea) -
<toolkitcontrols:Carousel ItemsSource="{x:Bind ButtonInfoCollection}">
<toolkitcontrols:Carousel.ItemTemplate>
<DataTemplate x:DataType="local:ButtonInfo">
<local:NavigationMenuButton NavigateToPageCommand="{Binding DataContext.NavigateToPageCommand, ElementName=MyPageName}"
NavigateToPageCommandParameter="{x:Bind PageType}"
MenuName="{x:Bind MenuName}"
SymbolPath="{x:Bind Symbol}" />
</DataTemplate>
</toolkitcontrols:Carousel.ItemTemplate>
</toolkitcontrols:Carousel>
With the structure above in mind, you just need to expose these properties as dependency properties in your NavigationMenuButton user control. See below as a simple example -
NavigationMenuButton XAML
<UserControl x:Class="DesignTest.NavigationMenuButton">
<!--If any of the properties can be updated, change the binding Mode to OneWay-->
<Button Command="{x:Bind NavigateToPageCommand, Mode=OneWay}" CommandParameter="{x:Bind NavigateToPageCommandParameter}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Image x:Name="SymbolImage" Stretch="UniformToFill" />
<TextBlock Text="{x:Bind MenuName, FallbackValue='JORNADAS EXTRAORDINARIAS', TargetNullValue='JORNADAS EXTRAORDINARIAS'}" Grid.Column="1" />
</Grid>
</Button>
</UserControl>
NavigationMenuButton Code-behind
public sealed partial class NavigationMenuButton : UserControl
{
public NavigationMenuButton()
{
InitializeComponent();
}
public ICommand NavigateToPageCommand
{
get => (ICommand)GetValue(NavigateToPageCommandProperty);
set => SetValue(NavigateToPageCommandProperty, value);
}
public static readonly DependencyProperty NavigateToPageCommandProperty = DependencyProperty.Register(
"NavigateToPageCommand", typeof(ICommand), typeof(NavigationMenuButton), new PropertyMetadata(null));
public object NavigateToPageCommandParameter
{
get => GetValue(NavigateToPageCommandParameterProperty);
set => SetValue(NavigateToPageCommandParameterProperty, value);
}
public static readonly DependencyProperty NavigateToPageCommandParameterProperty = DependencyProperty.Register(
"NavigateToPageCommandParameter", typeof(object), typeof(NavigationMenuButton), new PropertyMetadata(null));
public string MenuName
{
get => (string)GetValue(MenuNameProperty);
set => SetValue(MenuNameProperty, value);
}
public static readonly DependencyProperty MenuNameProperty = DependencyProperty.Register(
"MenuName", typeof(string), typeof(NavigationMenuButton), new PropertyMetadata(null));
public string SymbolPath
{
get => (string)GetValue(SymbolPathProperty);
set => SetValue(SymbolPathProperty, value);
}
public static readonly DependencyProperty SymbolPathProperty = DependencyProperty.Register(
"SymbolPath", typeof(string), typeof(NavigationMenuButton), new PropertyMetadata(null, (s, e) =>
{
// We don't do the x:Bind for this property in XAML because the Image control's Source property
// doesn't accept a string but a BitmapImage, so one workaround is to do the conversion here.
var self = (NavigationMenuButton)s;
var image = self.SymbolImage;
var symbolPath = (string)e.NewValue;
image.Source = new BitmapImage(new Uri(self.BaseUri, symbolPath ?? "/Assets/default_path"));
}));
}
Note you will need to include a PageType property in your ButtonInfo class for navigation purpose.
public Type PageType { get; set; }
I personally don't like having a navigation command defined at the item level (i.e in the ButtonInfo class), instead, I use an ElementName binding in the Carousel's data template to search up a level and bind to the NavigateToPageCommand defined in the page's DataContext, which is the page's ViewModel.
This means that this ViewModel will have both ButtonInfoCollection and NavigateToPageCommand defined like below -
public ObservableCollection<ButtonInfo> ButtonInfoCollection { get; } = new ObservableCollection<ButtonInfo>
{
new ButtonInfo { MenuName = "New Menu", PageType = typeof(BlankPage1), Symbol = "/Assets/StoreLogo.png" }
};
public DelegateCommand<Type> NavigateToPageCommand { get; } = new DelegateCommand<Type>(type =>
App.Frame.Navigate(type));
I hope this all makes sense. Good luck!
Ok, Firstable to Dilmah its always posible to build a reusable usercontrol within any itemTemplate. And I'll show you how right now and right here.
I have come up with two solution the first solution its inspire in what I was looking for after reading a lot about databing {x:Bind} and {Binding} markup, I was able to learn how to create a reusable UserControlTemplate
SOLUTION No.1
First I'll show you how to create a Navigation menu with a carusel control, which can be found at nuget package Microsoft.Toolkit.Uwp.UI.Control
so this is my code now on my MainMenuPage reference to carusel control:
<toolkitcontrols:Carousel x:Name="NavigationMenuCarouselPanel"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal"
ItemsSource="{x:Bind ViewModel.NavMenuButtonVMs}"
ItemMargin="25"
ItemDepth="160"
ItemRotationX="180"
ItemRotationY="25"
ItemRotationZ="0"
SelectedIndex="0"
Grid.Row="1">
<toolkitcontrols:Carousel.EasingFunction>
<CubicEase EasingMode="EaseOut"/>
</toolkitcontrols:Carousel.EasingFunction>
<toolkitcontrols:Carousel.ItemTemplate>
<DataTemplate>
<usercontrolvm:NavigationMenuButtonTemplate/>
</DataTemplate>
</toolkitcontrols:Carousel.ItemTemplate>
</toolkitcontrols:Carousel>
this important part of this code is at ItemSource property which is x:Bind to my NavMenuButtonVms ObservableCollection, and my Usercontrol which is wrapped withing Carousel.ItemTemplate and DataTemplate tags which will allows us to be able to reuse our code and create a N number of controls within our list.
the next is my ViewModel for my MainMenuPage:
public class MainMenuPageViewModel : Mvvm.ViewModelBase
{
ObservableCollection<NavigationMenuButtonTemplateViewModel> _NavMenuButtonVMs = default(ObservableCollection<NavigationMenuButtonTemplateViewModel>);
public MainMenuPageViewModel()
{
Shell.HamburgerMenu.IsFullScreen = false;
NavMenuButtonVMs = GetNavMenuButtonInfo();
}
public string Title => GetLocalizeString("MainMenuPageViewModelTitle");
public ObservableCollection<NavigationMenuButtonTemplateViewModel> NavMenuButtonVMs
{
get { return _NavMenuButtonVMs; }
private set { Set(ref _NavMenuButtonVMs, value); }
}
public override Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary<string, object> state)
{
NavigationService.ClearHistory();
return base.OnNavigatedToAsync(parameter, mode, state);
}
}
As you can see I initialize my ObservableCollection within my constructor.
The method GetNavMenuButton() is a static class in a Helpers Namespace but i'll show you the code so you can have an idea of how to seed the list also you can notice that I'm not calling the static class that because i'm using C# 6.0 syntax where you can call directly static methods within your class.
you can add a using statement for static classes like this one:
using static Ceneam.Helpers.NavigationMenuButtonViewModelHelper;
this statement allows you to use a static method like this:
GetNavMenuButtonInfo();
instead of this:
NavigationMenuButtonViewModelHelper.GetNavMenuButtonInfo();
I explained this in case you dont understand my code.
then I'll create my usercontrol which I'll show you the xaml, xaml.cs and also the viewmodel.
pay attention to the binding markup at usercontrol since you'll have to quit using x:Bind within a reusable usercontrol.
This is my NavigationMenuButtonTemplate.xaml
<UserControl
x:Class="Ceneam.UserControlViews.NavigationMenuButtonTemplate"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Ceneam.UserControlViews"
xmlns:vm="using:Ceneam.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="400"
d:DesignWidth="400">
<Grid VerticalAlignment="Center"
HorizontalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button x:Name="NavigationMenuTemplate"
Command="{Binding NavigateToPageCommand, Mode=OneWay}">
<Grid x:Name="ButtonLayout">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image x:Name="NavigationMenuIconImage"
Source="{Binding ButtonInfo.Symbol, Mode=OneWay, FallbackValue='ms-appx:///Assets/AssetsMainMenuPage/OverTimeMoneyWhite256x256.png'}"
PointerEntered="NavigationMenuIconImage_PointerEntered"/>
<TextBlock x:Name="NavigationMenuButtonNameTextBlock"
Text="{Binding ButtonInfo.MenuName, Mode=OneWay, FallbackValue='JORNADAS EXTRAORDINARIAS'}"/>
<TextBlock x:Name="NavigationMenuButtonBenefitKindTextBlock"
Text="{Binding ButtonInfo.BenefitKind, Mode=OneWay, FallbackValue='Subscripción'}"/>
<TextBlock x:Name="NavigationMenuButtonStatusTextBlock"
Text="{Binding ButtonInfo.Status, Mode=OneWay, FallbackValue='Vigente'}"/>
</Grid>
</Button>
</Grid>
as you can see I'm using binding markup only and the reason is because I use a viewmodel with a parameter that right i had to create a dependency on my usercontrol:
public class NavigationMenuButtonTemplateViewModel : Mvvm.ViewModelBase
{
ButtonInfo _ButtonInfo = default(ButtonInfo);
public NavigationMenuButtonTemplateViewModel() { }
public NavigationMenuButtonTemplateViewModel(ButtonInfo buttonInfo)
{
ButtonInfo = new ButtonInfo
{
BenefitKind = buttonInfo.BenefitKind,
Status = buttonInfo.Status,
MenuName = buttonInfo.MenuName,
Symbol = buttonInfo.Symbol
};
}
public ButtonInfo ButtonInfo
{
get { return _ButtonInfo; }
set { Set(ref _ButtonInfo, value); }
}
public DelegateCommand NavigateToPageCommand => new DelegateCommand(async () => { await ExecuteNavigateToPageCommand(); });
private async Task ExecuteNavigateToPageCommand()
{
var message = new MessageDialog("Test");
await message.ShowAsync();
}
}
since you create a contructor with a parameter in the viewmodel I wasnt able to create a strongly type bind with that constructor that is the main reason that made me leave behind x:bind markup for my usercontrol that means that you cant use x:bind methods on events. you will have to use styling methods within the xaml.cs file of your usercontrol.
If you declare in you xaml something like this:
<UserControl.DataContext>
<vm:usercontrol x:Name=ViewModel/>
<UserControl.DataContext>
it will always trigger your parameterless constructor getting rid of your init values of even worse getting NullReferenceExceptions, also you could use DataContext for DesignTime Data but it has to be declare at the beginning of your file and I wont center on it here.
and finally at my static class is where I create my UC (usercontrol) with a parameter in them this is my static class:
public static class NavigationMenuButtonViewModelHelper
{
public static ObservableCollection<NavigationMenuButtonTemplateViewModel> GetNavMenuButtonInfo()
{
var data = new ObservableCollection<NavigationMenuButtonTemplateViewModel>();
AddNavMenuButtonItem(data,
new NavigationMenuButtonTemplateViewModel(new ButtonInfo
{
Symbol = #"ms-appx:///Assets/AssetsMainMenuPage/SatSunBonusWhite256x256.png",
MenuName = "PRIMAS SABATINAS Y DOMINICALES",
BenefitKind = "Subscripción",
Status = "Vigente"
}));
AddNavMenuButtonItem(data,
new NavigationMenuButtonTemplateViewModel(new ButtonInfo
{
Symbol = #"ms-appx:///Assets/AssetsMainMenuPage/OverTimeMoneyWhite256x256.png",
MenuName = "JORNADAS EXTRAORDINARIAS",
BenefitKind = "Subscripción",
Status = "Vigente"
}));
AddNavMenuButtonItem(data,
new NavigationMenuButtonTemplateViewModel(new ButtonInfo
{
Symbol = #"ms-appx:///Assets/AssetsMainMenuPage/VacationBonusWhite256x256.png",
MenuName = "PRIMA VACACIONAL",
BenefitKind = "Gratuito",
Status = "Vigente"
}));
AddNavMenuButtonItem(data,
new NavigationMenuButtonTemplateViewModel(new ButtonInfo
{
Symbol = #"ms-appx:///Assets/AssetsMainMenuPage/PecoWhite256x256.png",
MenuName = "PERMISOS ECONOMICOS",
BenefitKind = "Gratuito",
Status = "Vigente"
}));
AddNavMenuButtonItem(data,
new NavigationMenuButtonTemplateViewModel(new ButtonInfo
{
Symbol = #"ms-appx:///Assets/AssetsMainMenuPage/PunctualityBonusWhite256x256.png",
MenuName = "INCENTIVO PUNTUALIDAD Y ASISTENCIA",
BenefitKind = "Gratuito",
Status = "Vigente"
}));
AddNavMenuButtonItem(data,
new NavigationMenuButtonTemplateViewModel(new ButtonInfo
{
Symbol = #"ms-appx:///Assets/AssetsMainMenuPage/BonForSeniorityWhite256x256.png",
MenuName = "BONO DE ANTIGUEDAD",
BenefitKind = "Gratuito",
Status = "Vigente"
}));
AddNavMenuButtonItem(data,
new NavigationMenuButtonTemplateViewModel(new ButtonInfo
{
Symbol = #"ms-appx:///Assets/AssetsMainMenuPage/WageIncreaseWhite256x256.png",
MenuName = "RETROACTIVO SUELDO",
BenefitKind = "Gratuito",
Status = "Vigente"
}));
return data;
}
private static void AddNavMenuButtonItem(ObservableCollection<NavigationMenuButtonTemplateViewModel> data, NavigationMenuButtonTemplateViewModel item)
{
data.Add(item);
}
}
also if you wish to style properties programatically you should do it at xaml.cs like for example like this one:
public sealed partial class NavigationMenuButtonTemplate : UserControl
{
public NavigationMenuButtonTemplate()
{
this.InitializeComponent();
}
private void NavigationMenuIconImage_PointerEntered(object sender, PointerRoutedEventArgs e)
{
var image = (Image)sender;
var bitmapImage = image.Source as BitmapImage;
var uri = bitmapImage?.UriSource;
var uriPath = uri?.AbsolutePath;
var newUriPath = $#"ms-appx://{uriPath.Replace("White", "Black")}";
image.Source = new BitmapImage(new Uri(newUriPath, UriKind.RelativeOrAbsolute));
}
}
**SOLUTION No.2: **
another solution could be using usercontrols with dependency properties like this one:
<toolkitcontrols:Carousel x:Name="NavigationMenuCarouselPanel"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal"
ItemSource="{x:Bind ViewModel.MenuList}"
ItemMargin="25"
ItemDepth="160"
ItemRotationX="180"
ItemRotationY="25"
ItemRotationZ="0"
SelectedIndex="0"
Grid.Row="1">
<toolkitcontrols:Carousel.EasingFunction>
<CubicEase EasingMode="EaseOut"/>
</toolkitcontrols:Carousel.EasingFunction>
<usercontrolvm:NavigationMenuButtonTemplate ButtonInfo="{x:Bind ViewModel.MenuList[0],Mode=OneWay}"
NavigateToPageCommand = "{x:Bind ViewModel.NavigateToPageCommand}"/>
You will have to create a NavigationMenuButtonTemplate.xaml.cs with dependency properties like this one:
public sealed partial class NavigationMenuButtonTemplate : UserControl
{
public NavigationMenuButtonTemplate()
{
this.InitializeComponent();
}
public DelegateCommand NavigateToPageCommand
{
get { return (DelegateCommand)GetValue(NavigateToPageCommandProperty); }
set { SetValue(NavigateToPageCommandProperty, value); }
}
// Using a DependencyProperty as the backing store for NavigateToPageCommand. This enables animation, styling, binding, etc...
public static readonly DependencyProperty NavigateToPageCommandProperty =
DependencyProperty.Register("NavigateToPageCommand",
typeof(DelegateCommand),
typeof(NavigationMenuButtonTemplate),
new PropertyMetadata(0));
public ButtonInfo ButtonInfo
{
get { return (ButtonInfo)GetValue(ButtonInfoProperty); }
set { SetValue(ButtonInfoProperty, value); }
}
// Using a DependencyProperty as the backing store for ButtonInfo. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ButtonInfoProperty =
DependencyProperty.Register("ButtonInfo",
typeof(ButtonInfo),
typeof(NavigationMenuButtonTemplate),
new PropertyMetadata(0));
}
I dont like this solution because I'll have to repeat code at xaml file but its a good choice too.
Hope you like my answer I think it can be used by many of us and its appliable to many other controls with your endless imagination.
I'm trying to build a generic Command that can access properties from my ViewModel. On my Window are several TextBoxes, each TextBoxhas a Button next to it. When the Button is clicked I show an OpenFileDialog and set the Text of the TextBox to the selected files path. The TextBoxitself has a Binding to a property in the ViewModel. Currently this is implemented with a Command in the ViewModel. The Buttons all call the same Commandbut each Button has a its property CommandParameter set to the TextBox that will receive the filepath. The drawback of this is, that I need to cast the parameter from the Commandexecution to a TextBox and then set its Textproperty. My question is now, if I can't somehow bind my 'Command' to the same property the TextBoxis bound to. Here is what I currently do:
XAML
<TextBox Text="{Binding SettingsPath}" x:Name="txtSettingsPath"></TextBox>
<Button Command="{Binding OpenFile}"
CommandParameter="{Binding ElementName=txtSettingsPath}">...</Button>
C#
public ICommand OpenFile
{
get
{
bool CanExecuteOpenFileCommand()
{
return true;
}
CommandHandler GetOpenFileCommand()
{
return new CommandHandler((o) =>
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Multiselect = false;
if (!string.IsNullOrEmpty(SettingsPath) && File.Exists(settingsPath))
{
ofd.InitialDirectory = Path.GetDirectoryName(SettingsPath);
}
if(ofd.ShowDialog() == true)
{
if(o is TextBox txt)
{
txt.Text = ofd.FileName;
}
}
}, CanExecuteOpenFileCommand);
}
return GetOpenFileCommand();
}
}
In the XAML I would like to have something like this:
<TextBox Text="{Binding SettingsPath}"></TextBox>
<Button Command="{Binding OpenFile}"
CommandParameter="{Binding SettingsPath}">...</Button>
Here's what I was talking about in comments:
The "little viewmodel". I added a Label property because in my test project, they all looked the same. That doesn't have to be part of this viewmodel.
public class SettingsPathSelectorViewModel : ViewModelBase
{
#region Label Property
private String _label = default(String);
public String Label
{
get { return _label; }
set
{
if (value != _label)
{
_label = value;
OnPropertyChanged();
}
}
}
#endregion Label Property
#region SettingsPath Property
private String _settingsPath = null;
public String SettingsPath
{
get { return _settingsPath; }
set
{
if (value != _settingsPath)
{
_settingsPath = value;
OnPropertyChanged();
}
}
}
#endregion SettingsPath Property
public ICommand OpenFile
{
get
{
bool CanExecuteOpenFileCommand()
{
return true;
}
// We're no longer using the parameter, since we now have one
// command per SettingsPath.
CommandHandler GetOpenFileCommand()
{
return new CommandHandler((o) =>
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Multiselect = false;
if (!string.IsNullOrEmpty(SettingsPath) && System.IO.File.Exists(SettingsPath))
{
ofd.InitialDirectory = System.IO.Path.GetDirectoryName(SettingsPath);
}
if (ofd.ShowDialog() == true)
{
SettingsPath = ofd.FileName;
}
}, o => CanExecuteOpenFileCommand());
}
return GetOpenFileCommand();
}
}
}
A quickie main viewmodel for demo purposes. We'll illustrate two different ways you could expose these things: Either as named properties, or a collection of varying size displayed in an ItemsControl.
public class MainViewModel : ViewModelBase
{
public SettingsPathSelectorViewModel FirstPath { get; } = new SettingsPathSelectorViewModel() { Label = "First Path" };
public SettingsPathSelectorViewModel SecondPath { get; } = new SettingsPathSelectorViewModel() { Label = "Second Path" };
public ObservableCollection<SettingsPathSelectorViewModel> SettingsPaths { get; } = new ObservableCollection<SettingsPathSelectorViewModel>
{
new SettingsPathSelectorViewModel() { Label = "First Collection Path" },
new SettingsPathSelectorViewModel() { Label = "Second Collection Path" },
new SettingsPathSelectorViewModel() { Label = "Third Collection Path" },
};
}
XAML:
<Window.Resources>
<DataTemplate DataType="{x:Type local:SettingsPathSelectorViewModel}">
<!-- GroupBox and Label are optional -->
<GroupBox Header="{Binding Label}">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding SettingsPath}" />
<Button
Content="..."
Command="{Binding OpenFile}"
HorizontalAlignment="Left"
MinWidth="40"
Margin="4,0,0,0"
/>
</StackPanel>
</GroupBox>
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<ContentControl Content="{Binding FirstPath}" />
<ContentControl Content="{Binding SecondPath}" />
</StackPanel>
<ItemsControl
ItemsSource="{Binding SettingsPaths}"
/>
</StackPanel>
</Grid>
Here's what I mean about omitting Label and the GroupBox:
<Window.Resources>
<DataTemplate DataType="{x:Type local:SettingsPathSelectorViewModel}">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding SettingsPath}" />
<Button
Content="..."
Command="{Binding OpenFile}"
HorizontalAlignment="Left"
MinWidth="40"
Margin="4,0,0,0"
/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel Orientation="Vertical">
<Label>First Path</Label>
<ContentControl Content="{Binding FirstPath}" />
<Label>Second Path</Label>
<ContentControl Content="{Binding SecondPath}" />
</StackPanel>
</Grid>
I am trying to create a basic colour change UserControl utilising my custom slider control, LabelSlider. I can get the colour to be set via code, however if I move the slider or enter text into the TextBox, it doesn't update the colour.
LabelSlider XAML:
<Slider x:Name="ucSlider" x:FieldModifier="private" Margin="{Binding SliderSpacing, FallbackValue=5, Converter={StaticResource DoubleToMargin}}" VerticalAlignment="Center" Grid.Column="1" Width="{Binding SliderWidth}" MaxWidth="{Binding SliderWidth}" FontFamily="Segoe UI" Value="{Binding Value, Mode=TwoWay}" SmallChange="1" Maximum="255"/>
<TextBox x:Name="ucTextBox" x:FieldModifier="private" Text="{Binding Value, TargetNullValue=0, Mode=TwoWay}" Margin="{Binding TextBoxSpacing, FallbackValue=5, Converter={StaticResource DoubleToMargin}}" Grid.Column="2" PreviewKeyDown="LabelSlider_PreviewKeyDown" PreviewTextInput="LabelSlider_PreviewTextInput" MaxLength="3" Width="30" MaxWidth="30" FontFamily="Segoe UI" TextChanged="LabelSlider_TextChanged"/>
LabelSlider Code Behind:
public int Value
{
get { return (int)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register
(
"Value",
typeof(int),
typeof(LabelSlider),
new PropertyMetadata(null)
);
ColourSelection XAML:
<UserControl x:Class="Homuli.UserControls.ColourSelection"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Homuli.UserControls"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" x:Name="colourSelection">
<StackPanel Margin="5,0,0,0" DataContext="{Binding ElementName=colourSelection}">
<local:LabelSlider Label="R" SliderSpacing="6" SliderWidth="100" Value="{Binding R, FallbackValue=255, TargetNullValue=0}" Foreground="White" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0,-4"/>
<local:LabelSlider Label="G" SliderSpacing="5" SliderWidth="100" Value="{Binding G, FallbackValue=0, TargetNullValue=0}" Foreground="White" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0,-4"/>
<local:LabelSlider Label="B" SliderSpacing="6" SliderWidth="100" Value="{Binding B, FallbackValue=0, TargetNullValue=0}" Foreground="White" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0,-4"/>
<StackPanel Orientation="Horizontal">
<local:LabelTextBox Label="Hex" Spacing="15" TextBoxWidth="50" Text="{Binding Hex}" MaxLength="6" Foreground="White" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<Rectangle Fill="{Binding Colour}" Height="26" Width="26" Margin="26,0,0,0" Stroke="Black" />
</StackPanel>
</StackPanel>
ColourSelection Code Behind:
public partial class ColourSelection : UserControl
{
public ColourSelection()
{
InitializeComponent();
}
public SolidColorBrush Colour
{
get { return (SolidColorBrush)GetValue(ColourProperty); }
set { SetValue(ColourProperty, value); }
}
public static readonly DependencyProperty ColourProperty = DependencyProperty.Register
(
"Colour",
typeof(SolidColorBrush),
typeof(ColourSelection),
new PropertyMetadata(null)
);
public int R
{
get { return (int)GetValue(RProperty); }
set
{
if (value <= 255 && value >= 0)
{
SetValue(RProperty, value);
UpdateColour();
}
}
}
public static readonly DependencyProperty RProperty = DependencyProperty.Register
(
"R",
typeof(int),
typeof(ColourSelection),
new PropertyMetadata()
);
public int G
{
get { return (int)GetValue(GProperty); }
set
{
if (value <= 255 && value >= 0)
{
SetValue(GProperty, value);
UpdateColour();
}
}
}
public static readonly DependencyProperty GProperty = DependencyProperty.Register
(
"G",
typeof(int),
typeof(ColourSelection),
new PropertyMetadata(null)
);
public int B
{
get { return (int)GetValue(BProperty); }
set
{
if (value <= 255 && value >= 0)
{
SetValue(BProperty, value);
UpdateColour();
}
}
}
public static readonly DependencyProperty BProperty = DependencyProperty.Register
(
"B",
typeof(int),
typeof(ColourSelection),
new PropertyMetadata(null)
);
void UpdateColour()
{
Colour = new SolidColorBrush(Color.FromRgb((byte)R, (byte)G, (byte)B));
}
}
Any help will be greatly appreciated.
when DP is changing, it happens via static object BProperty. Code in setter
if (value <= 255 && value >= 0)
{
SetValue(RProperty, value);
UpdateColour();
}
is not executed.
you need to add property change callback (for R,G,B properties):
public int B
{
get { return (int)GetValue(BProperty); }
set { SetValue(BProperty, value); }
}
public static readonly DependencyProperty BProperty = DependencyProperty.Register
(
"B",
typeof(int),
typeof(ColourSelection),
new PropertyMetadata(0, PropertyChangedCallback)
);
private static void PropertyChangedCallback(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
if ((int) e.NewValue <= 255 && (int) e.NewValue >= 0)
((ColourSelection) obj).UpdateColour();
}
also I wouldn't recommend to replace dataContext in userControl like this DataContext="{Binding ElementName=colourSelection}". Instead use ElementName in bindings:
<local:LabelSlider Label="R" SliderSpacing="6" SliderWidth="100"
Value="{Binding R, ElementName=colourSelection, FallbackValue=255, TargetNullValue=0}"
I want to generate ListItemBox using DataTemplate but items are not generating. Please guide me where is the mistake. I have following code in MainWindow.xaml.
<Window x:Class="Offline_Website_Downloader.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:bd="clr-namespace:Offline_Website_Downloader"
Title="Offline Website Downloader" Background="#f5f6f7" Height="500" Width="800"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<bd:BindingController x:Key="BindingControllerKey" />
<DataTemplate x:Key="DownloadedWebsitesListBox">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal" Width="Auto">
<TextBlock FontWeight="Bold" FontSize="18" Width="480">
<Hyperlink NavigateUri="http://google.com">
<Label Content="{Binding Path=WebsiteTitle}" />
</Hyperlink>
</TextBlock>
<TextBlock Width="132" TextAlignment="right">
<TextBlock Text="Remaining Time: "/>
<TextBlock Name="TimeRemaining" Text="js"/>
</TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal">
<ProgressBar Name="progress1" Maximum="100" Minimum="0" Value="30" Background="#FFF" Width="612" Height="10" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock HorizontalAlignment="Left" Width="450">Status: <TextBlock Text="{Binding Path=Status}"/></TextBlock>
<TextBlock Width="162" TextAlignment="right">
<TextBlock Text="Downloading Speed: "/>
<TextBlock Name="DownloadingSpeed" Text="{Binding Path=DownloadingSpeed}"/>
</TextBlock>
</StackPanel>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox Width="Auto"
Name="WebsiteList"
Grid.Column="1"
Grid.Row="2"
Grid.RowSpan="2"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource DownloadedWebsitesListBox}"
Margin="0,0,0,0">
</ListBox>
</Grid>
</window>
and MainWindow.xaml.cs
BindingController bc = new BindingController();
public MainWindow()
{
InitializeComponent();
bc.DownloadingSpeed = "40kb/s";
bc.WebsiteTitle = "WebsiteTitle";
bc.Status = "Downloading";
DataContext = bc;
}
and BindingController.cs
public class BindingController
{
public BindingController()
{
}
private string _WebsiteTitle;
public string WebsiteTitle
{
set { _WebsiteTitle = value; }
get { return _WebsiteTitle; }
}
private string _Status;
public string Status
{
set { _Status = value ; }
get { return _Status ; }
}
private string _DownloadStartDate;
public string DownloadStartDate
{
set { _DownloadStartDate = value; }
get { return _DownloadStartDate ; }
}
private string _DownloadingSpeed = "0 kb/s";
public string DownloadingSpeed
{
set { _DownloadingSpeed = value; }
get { return _DownloadingSpeed; }
}
}
Your problem is that you're binding a ListBox to an object that contains several properties, but really only represents a single object/state. The ListBox expects to display a list of items (i.e. IList, IBindingList, IEnumerable, ObservableCollection).
Assuming you want to display more than one download at a time, which I'm assuming given that you're using a ListBox, I would refactor the download properties into a separate class. You will also need to implement INotifyPropertyChanged on your properties so that when the values are changed, they will be shown in the UI.
public class Download : INotifyPropertyChanged
{
private string _WebsiteTitle;
public string WebsiteTitle
{
get { return _WebsiteTitle; }
set
{
if (_WebsiteTitle == value)
return;
_WebsiteTitle = value;
this.OnPropertyChanged("WebsiteTitle");
}
}
private string _Status;
public string Status
{
get { return _Status; }
set
{
if (_Status == value)
return;
_Status = value;
this.OnPropertyChanged("Status");
}
}
private string _DownloadStartDate;
public string DownloadStartDate
{
get { return _DownloadStartDate; }
set
{
if (_DownloadStartDate == value)
return;
_DownloadStartDate = value;
this.OnPropertyChanged("DownloadStartDate");
}
}
private string _DownloadingSpeed = "0 kb/s";
public string DownloadingSpeed
{
get { return _DownloadingSpeed; }
set
{
if (_DownloadingSpeed == value)
return;
_DownloadingSpeed = value;
this.OnPropertyChanged("DownloadingSpeed");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if(this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
The new BindingController:
public class BindingController
{
public BindingController()
{
this.Downloads = new ObservableCollection<Download>();
}
public ObservableCollection<Download> Downloads { get; private set; }
}
Setting up the bindings in XAML:
<ListBox Width="Auto"
Name="WebsiteList"
Grid.Column="1"
Grid.Row="2"
Grid.RowSpan="2"
ItemsSource="{Binding Downloads}"
ItemTemplate="{StaticResource DownloadedWebsitesListBox}"
Margin="0,0,0,0">
</ListBox>
Initializing the collection in MainWindow
Download download = new Download();
download.DownloadingSpeed = "40kb/s";
download.WebsiteTitle = "WebsiteTitle";
download.Status = "Downloading";
bc.Downloads.Add(download);
this.DataContext = bc;