Display image based on combobox item selection in wpf - c#-4.0

I have one combo box in which I have some items like:
Item1
Item2
Item3
Corresponding every item there is image like item1 has image img1.jpg, item2 has image img2.jpg and item3 has image img3.jpg. When we select item from combox it will show their corresponding image in label.

I got answer of my question and here it's:
<xmlns:local="clr-namespace:ImageMVVM_Learning"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:EnumToImageConverter x:Key="conv"/>
</Window.Resources>
<Grid>
<StackPanel>
<ComboBox x:Name="combo" ItemsSource="{Binding MyProperty}"/>
<Image Source="{Binding ElementName=combo,Path=SelectedValue,Converter={StaticResource conv}}"/>
</StackPanel>
</Grid>
</Window>
Do this in your viewmodel class:
public enum MyEnum
{
A,
B,
C
}
public class EnumToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
switch ((MyEnum)value)
{
case MyEnum.A:
return new BitmapImage(new Uri(#"Images\A.png", UriKind.Relative));
case MyEnum.B:
return new BitmapImage(new Uri(#"Images\B.png", UriKind.Relative));
}
}
return new BitmapImage(new Uri(#"Images\A.png", UriKind.Relative));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}

Related

How can I dynamically apply Lightweight styling to NavigationView?

Changing the NavigationViewContentMargin dynamically, which determines the top margin of the NavigationView, does not change the appearance of the NavigationView dynamically.
Is there a way to dynamically change the top margin of NavigationView from the code?
top margin of NavigationView
You need to get the target Grid "NavigationViewContentMargin" is applied to. This Grid is named "ContentGrid".
The tricky part is that NavigationViews have 2 Grids with the name "ContentGrid". The "ContentGrid" we need is the one that is inside a Grid with the name "ContentRoot". BTW, the other one is inside "Settings".
First, we need to get the "ContentRoot" Grid, then get the "ContentGrid" we need.
Check this example:
MainWindow.xaml
<Window
x:Class="NavigationViewTests.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Microsoft.UI.Xaml.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:NavigationViewTests"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid ColumnDefinitions="*,Auto">
<Grid.Resources>
<Thickness x:Key="NavigationViewContentMargin">0,48,0,0</Thickness>
</Grid.Resources>
<NavigationView
x:Name="NavigationViewControl"
Grid.Column="0"
IsBackButtonVisible="Visible">
<TextBlock Text="Content" />
</NavigationView>
<StackPanel Grid.Column="1">
<NumberBox
x:Name="NavigationViewContentGridTopMarginNumber"
Header="NavigationView Top Margin"
ValueChanged="NumberBox_ValueChanged" />
</StackPanel>
</Grid>
</Window>
MainWindow.xaml.cs
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System.Linq;
namespace NavigationViewTests;
public sealed partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
this.NavigationViewControl.Loaded += (s, e) =>
{
this.NavigationViewContentGrid = GetNavigationViewContentGridInsideContentGrid();
if (this.NavigationViewContentGrid is not null)
{
this.NavigationViewContentGridTopMarginNumber.Value = this.NavigationViewContentGrid.Margin.Top;
}
};
}
private Grid? NavigationViewContentGrid { get; set; }
private Grid? GetNavigationViewContentGridInsideContentGrid()
{
Grid? contentRoot = this.NavigationViewControl
.FindChildrenOfType<Grid>()
.Where(x => x.Name == "ContentRoot")
.FirstOrDefault();
return contentRoot?
.FindChildrenOfType<Grid>()
.Where(x => x.Name == "ContentGrid")
.FirstOrDefault();
}
private void NumberBox_ValueChanged(NumberBox _, NumberBoxValueChangedEventArgs args)
{
if (NavigationViewContentGrid is not null)
{
Thickness currentMargin = this.NavigationViewContentGrid.Margin;
this.NavigationViewContentGrid.Margin = new Thickness(
currentMargin.Left,
args.NewValue,
currentMargin.Right,
currentMargin.Bottom);
}
}
}

Dependency property for ItemsSource column to set DataTrigger condition

How would I properly bound to ItemsSource column via Dependency property of control (Datagrid), in order to set It's DataTrigger working?
My goal that works without dependency property:
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding NAME}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
And this is how I want It to work:
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding HideRow, ElementName =_myGrid}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
My dependency property :
public static DependencyProperty HideRowProperty =
DependencyProperty.Register("HideRow", typeof(PersonsModel), typeof(My_DataGrid),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
///<summary>Property for hiding rows, based on column name</summary>
public PersonsModel HideRow
{
get { return (PersonsModel)GetValue(HideRowProperty); }
set { SetValue(HideRowProperty, value); }
}
And this is how I try to bind control in XAML:
<ctl:My_DataGrid ItemsSource="{Binding Persons}" HideRow="{Binding Persons.NAME}">
More explanation: ItemsSource Persons is ObservableCollection of PersonsModel (which is type of Dependency property).
I'm getting BindingExpression path error: 'NAME' property not found on 'object' ''ObservableCollection`1' error.
Based on your comments, your goal is to tell DataGrid to hide the row that has some property value == null. Hence, you have to assign the property's name to HideRow property and use a converter which uses reflection to get the property value and affect the style trigger.
You have 2 options, the first one is simpler and you don't need the HideRow property at all:
Change RowStyle a bit, Here you will pass the name of the property as a ConverterParameter (so it might be Name, Age, etc...)
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Style.Triggers>
<DataTrigger Binding="{Binding ., Converter={StaticResource NullPropertyToBoolConverter}, ConverterParameter=Name}" Value="True">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
Where NullPropertyToBoolConverter is
public class NullPropertyToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is PersonsModel person && parameter is string propertyName)
{
return typeof(PersonsModel).GetProperty(propertyName)?.GetValue(person, null) == null;
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
The converter is defined in resources of one of DataGrid's parents (or in Application.xaml)
<converters:NullPropertyToBoolConverter x:Key="NullPropertyToBoolConverter" />
It is getting the value of Name, and return true if it was null, and this will cause the row visibility to be Collapsed.
This option is suitable because in both options you have to define RowStyle but here you don't need to define a dependency property.
Here you would change HideRow type to string instead of PersonsModel, and use it like this:
<ctl:My_DataGrid ItemsSource="{Binding Persons}" HideRow="Name" >
So, it might be Name, Age, etc...
You'd define <DataGrid.RowStyle> similar to this
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="Visibility">
<Setter.Value>
<MultiBinding Converter="{StaticResource NullPropertyToBoolConverter2}">
<Binding Path="DataContext" RelativeSource="{RelativeSource Mode=Self}" />
<Binding Path="HideRow" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGrid}" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</DataGrid.RowStyle>
and define the Converter to return Collapse if property's value is null, otherwise Visible.
public class NullPropertyToBoolConverter2 : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values[0] is Obj obj && values[1] is string str)
{
return typeof(Obj).GetProperty(str)?.GetValue(obj, null) == null ? Visibility.Collapsed : Visibility.Visible;
}
return Visibility.Visible;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

How to add ListBox Item Contextmenu in UWP

I am searching to add Context menu in every item of my listbox item. I know it was very easy in wp8 app using toolkit. However, Toolkit is not supported in uwp.
How can I add Context menu in uwp listbox item?
Thanks!
You can create ListBox.ItemTemplate with MenuFlyout, for example:
<ListBox.ItemTemplate>
<DataTemplate>
<Grid PointerEntered="Grid_PointerEntered" >
<FlyoutBase.AttachedFlyout>
<MenuFlyout>
<MenuFlyoutItem x:Name="EditButton"
Text="Edit"
Click="EditButton_Click"/>
<MenuFlyoutItem x:Name="DeleteButton"
Text="Delete"
Click="DeleteButton_Click"/>
</MenuFlyout>
</FlyoutBase.AttachedFlyout>
<TextBlock Text="{Binding Name}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
Handle the PointerEntered event to show Flyout when the pointer has been moved into an ListBoxItem:
private void Grid_PointerEntered(object sender, PointerRoutedEventArgs e)
{
FrameworkElement senderElement = sender as FrameworkElement;
FlyoutBase flyoutBase = FlyoutBase.GetAttachedFlyout(senderElement);
flyoutBase.ShowAt(senderElement);
}
Handle MenuFlyoutItem Click event:
private void EditButton_Click(object sender, RoutedEventArgs e)
{
var datacontext = (e.OriginalSource as FrameworkElement).DataContext;
//this datacontext is probably some object of some type T
}
private void DeleteButton_Click(object sender, RoutedEventArgs e)
{
var datacontext = (e.OriginalSource as FrameworkElement).DataContext;
//this datacontext is probably some object of some type T
}
Please check my feasible sample on Github

Set row color of Telerik Grid

I have used Telerik RadGrid to build a grid. The grid itself works but it is databound to a SQL database. I am trying to display rows with different colors.
Here is an example of what I am trying to do:
Here is what I have so far:
protected void SummaryGrid_ItemDataBound(object sender, GridItemEventArgs e)
{
if (e.Item is GridDataItem)
{
GridItem dataItem = e.Item;
if (dataItem["Red"].Text = "Red")
{
dataItem.BackColor = Color.Red;
}
}
}
Any help with this would be great.
Try the following code to change the color based on a particular value.
protected void RadGrid1_ItemDataBound(object sender, GridItemEventArgs e)
{
if (e.Item is GridDataItem)
{
GridDataItem dataItem = e.Item;
if (dataItem["Size"].Text == "1")
{
dataItem.BackColor = Drawing.Color.Red;
}
}
You can use style triggers to accomplish this.
App.xaml
<Style BasedOn="{StaticResource GridViewRowStyle}" TargetType="telerik:GridViewRow">
<Style.Triggers>
<DataTrigger Binding="{Binding YourObject.Size Converter={StaticResource ColorConverter}}" Value="Red">
<Setter Property="Background" Value="Red" />
<Setter Property="FontWeight" Value="Bold" />
</DataTrigger>
<DataTrigger Binding="{Binding YourObject.Size Converter={StaticResource ColorConverter}}" Value="Green">
<Setter Property="Background" Value="Green" />
</DataTrigger>
</Style.Triggers>
</Style>
ColorConverter.cs
public class ColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is int)
{
if ((int)value > 100)
return "Red";
else
return "Green";
}
else
return "Default";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
// TODO: Implement this method
throw new NotImplementedException();
}
}
Figured it out and its really simple actually, just going to say if the text equals what you need it to be then its a red row or if its something else then its whatever other color. Really easy but a lot of people have had this issue so hopefully this helps others.
if (e.Item is GridDataItem)
{
var item = (GridDataItem)e.Item;
if (item["Type"].Text == "RedRow")
{
item.BackColor = Color.Red;
}
else if(item["Type"].Text == "OrangeRow")
{
item.BackColor = Color.Orange;
}
}

What are the best practices to dynamically create views and add controls to view using MVVM pattern

Can anyone give out the best practices for playing around with controls at runtime such as creating a new view,adding views inside a view,adding controls to containers using MVVM pattern without breaking mvvm pattern??
I am using MVVMlight toolkit..
please help me out in this regard..
Thanks in advance......
This post discusses the strategies for creating views (dialogs) from your view models.
Edit:
From your comment I take it that you got an user interface that has an add and delete button. The add button should add an item (type ?) to a ItemsControl ... hope that's correct.
So, how would I do this, well I would create a view model that has an ObservableCollecion<ItemViewModel>. The ItemViewModle is the view mode that represents the item that should be added to the ItemsControl (so in your case the view model backing your "rangeView").
Then I would add two commands that handle the addition and deletion of items. Both commands just add/remove ItemViewModels from your collection.
To show the items in the view I would bind the ItemControl.ItemsSource property to the collection in your main view model (i.e. the one holding the ItemViewModel instances). The I would supply an ItemTemplate to render the items on the screen.
Ok, here is an example of what I think you are trying to do (at least conceptionally). Complete Source Code here. In the example I used a ListBox as it allows me easily to determine which item is selected, this depends on your szenario. Also note that you have complete freedom to customize the Template, the ItemPanelTemplate, and DataTemplate to fit your needs. You can even use this approacht to create PanoramaPages in WP7!
2nd edit: ItemsControl does not have a SelectedItem property. To get to it you have to use a control inheriting from Selector (e.g. a ListBox as I did) or you can use the Selector directly.
ItemViewModel:
public class ItemViewModel : ViewModelBase
{
#region [Name]
public const string NamePropertyName = "Name";
private string _name = null;
public string Name {
get {
return _name;
}
set {
if (_name == value) {
return;
}
var oldValue = _name;
_name = value;
RaisePropertyChanged(NamePropertyName);
}
}
#endregion
}
MainViewModel:
public class MainViewModel : ViewModelBase
{
public MainViewModel() {
if (IsInDesignMode) {
this.Items = new ObservableCollection<ItemViewModel>(Enumerable.Range(0, 10).Select((x, i) => new ItemViewModel() { Name = "Design Time Item " + i }));
} else {
// Code runs "for real"
}
}
#region [AddCommand]
private RelayCommand _addCommand;
public RelayCommand AddCommand {
get {
return _addCommand ?? (_addCommand = new RelayCommand(
() => {
this.Items.Add(new ItemViewModel() { Name = "New item - " + DateTime.Now });
}
));
}
}
#endregion
#region [DeleteCommand]
private RelayCommand _deleteCommand;
public RelayCommand DeleteCommand {
get {
return _deleteCommand ?? (_deleteCommand = new RelayCommand(
() => {
this.Items.Remove(this.SelectedItem);
},
() => { return this.SelectedItem != null; }
));
}
}
#endregion
#region [Items]
public const string ItemsPropertyName = "Items";
private ObservableCollection<ItemViewModel> _items = new ObservableCollection<ItemViewModel>();
public ObservableCollection<ItemViewModel> Items {
get {
return _items;
}
set {
if (_items == value) {
return;
}
var oldValue = _items;
_items = value;
RaisePropertyChanged(ItemsPropertyName);
}
}
#endregion
#region [SelectedItem]
public const string SelectedItemPropertyName = "SelectedItem";
private ItemViewModel _selectedItem = null;
public ItemViewModel SelectedItem {
get {
return _selectedItem;
}
set {
if (_selectedItem == value) {
return;
}
var oldValue = _selectedItem;
_selectedItem = value;
RaisePropertyChanged(SelectedItemPropertyName);
// important in SL to notify command that can execute has changed !
this.DeleteCommand.RaiseCanExecuteChanged();
}
}
#endregion
}
MainPage.xaml
<UserControl
x:Class="MvvmLight1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Height="300"
Width="300"
DataContext="{Binding Main, Source={StaticResource Locator}}"
>
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Skins/MainSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="0"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<!-- we are dealing with ItemViewModels now -->
<Border BorderThickness="0,0,0,1" BorderBrush="Gray" Padding="10,5">
<TextBlock Text="{Binding Name}"/>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Margin="5,10" Content="Add" Command="{Binding AddCommand}" Width="70"/>
<Button Margin="5,10" Content="Delete" Command="{Binding DeleteCommand}" Width="70"/>
</StackPanel>
</Grid>
</UserControl>

Resources