Display image if present in datagrid - c#

I am writing a program to display text and image associated with the text. The nature of data is such that I may or may not have image for every text. This is information is in collection of objects. Each object has text and path of image. If image is not there then path is empty. The class for the object is
public class MyInfo
{
public DateTime EntryDate { set; get; }
public string NoteText { set; get; }
public string ImagePath { set; get; }
}
I used DataGrid to display the information. 1st column shows text and the second shows image. If there is no image then the 2nd column is empty. This does not look nice and customer is asking to change the UI so that it should take the full row if there is no image. Also he wants to have clear separator between the rows. I already have alternating colors but does not go well with the both text and image in place.
Please suggest how to enhance the grid. If DataGrid is not the right control then what is the other control/approach to resolve it.
Thanks

Personally I would a ListBox and use the ListBox.ItemTemplate to define how the row will look. This will give greater flexibility and better achieve what you want. As Ashok said you will want to use a value converter to convert a empty string into a "Collapsed" visibility option.
Converter example:
public class EmptyStringToCollapsedConverter : IValueConverter
{
public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var s = (value as string);
return String.IsNullOrWhiteSpace(s)? Visibility.Collapsed : Visibility.Visible;
}
public override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}

Instead of using DataGrid, I suggest to use the ListBox with a DataTemplate, in a way similar to this:
<ListBox ItemsSource="{Binding Path=MyInfoCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Path=EntryDate}" />
<TextBlock Grid.Column="1" Text="{Binding Path=NoteText}" />
<Image Grid.Column="2" Source="{Binding Path=ImagePath}" />
</Grid>
</DataTemplate>
</ListBox .ItemTemplate>
</ListBox >
Where MyInfoCollection is an ObservableCollection of MyInfo objects.

Related

How to get new window height after resize in xaml

I have a UserControl that I've initialized with ResizeMode=CanResize but I need certain elements within the UserControl to depend on the window's height after the user resizes it by dragging the sides. Specifically, I need to the size of a TextBox to always have height that's 40 less than the window's height.
So how do I get this new window size after the user resizes?
Thanks!
NOTE:
Thanks to Pavel Anikhousk, using ActualHeight works. For future reference, this was my code:
<RowDefinition Height="{Binding ActualHeight, Converter={convs:ChatTextBoxSizeConverter}}" />
and ChatTextBoxSizeConverter.cs
namespace Converters
{
[ValueConversion(typeof(string), typeof(string))]
public class ChatTextBoxSizeConverter : MarkupExtension, IValueConverter
{
private static ChatTextBoxSizeConverter instance_;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (System.Convert.ToInt32(value) - 40);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return instance_ ?? (instance_ = new ChatTextBoxSizeConverter());
}
}
}
I'm not sure about how your layout is, but if you want to keep your window responsive and well designed this could be it:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<TextBox />
</Grid>
Note the TextBox will fill the entire space, but a 40px is always reserved at the bottom.

Binding items in ListBox in WP8

In a Windows Phone 8 app,
I have a listbox with 2 TextBlocks and a button.
I have a list of 2 strings and a boolean & I am able to bind the strings to the TextBlocks.
<ListBox Name="ListboxTest">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Key}" TextWrapping="Wrap"/>
<TextBlock Text="{Binding Value}" TextWrapping="Wrap"/>
<Button />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And this is the C# code to bind to the list box.
public class Detail
{
public string Key { get; set; }
public string Value { get; set; }
public bool check { get; set; }
}
public List<Detail> ClassList = new List<Detail>();
ListboxTest.ItemsSource = ClassList;
I want to display the button only when the boolean value is true.
How do I do it?
Take a look at this. Actually what you really need is a Converter by implementing the IValueConverter. This is also a good example where you could read about it. Bind the boolean value with the visibility property of the button and you are done! ;)
You can use boolean to visibility converter to hide, show button
Here are example:
public class BoolToVisibility : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var boolValue = false;
if (value != null) boolValue = (bool)value;
return boolValue ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
In app.xaml
<my:BoolToVisibility x:Key="BoolToVisibility"/>
In your data template
<Button Visibility="{Binding Path=YourBoolProperty,Converter={StaticResource BoolToVisibility}}>
Or, you could add this property to the Detail class:
public Visibility ButtonVisibility {
get {
return this.check == true ? Visibility.Visible : Visibility.Collapsed;
}
}
And then just bind the button's Visibility to the ButtonVisibility property without any converters.
<Button Visibility="{Binding ButtonVisibility}" />
Please try those use Triggers.
Various Triggers in windows phone Msdn.
Please use ObservableCollection in WP8 for binding instead of List.
Please make your properties are implemented with INotifyPropertyChanged If your Boolean property is not implemented with inotifypropertychanged the view will not know the value is changed.hence the Data trigger will not work.
Namespace
xmlns:interactivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ec="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
x:Class="XXX_XXXX"
<Button Content="My button"
Stretch="None"
HorizontalAlignment="Stretch"
VerticalAlignment="Top">
<interactivity:Interaction.Triggers>
<ec:DataTrigger Binding="{Binding Check}" Value="True">
<ec:ChangePropertyAction PropertyName="Visibility">
<ec:ChangePropertyAction.Value>
<Visibility>Visible</Visibility>
</ec:ChangePropertyAction.Value>
</ec:ChangePropertyAction>
</ec:DataTrigger>
<ec:DataTrigger Binding="{Binding Check}" Value="False">
<ec:ChangePropertyAction PropertyName="Visibility">
<ec:ChangePropertyAction.Value>
<Visibility>Collapsed</Visibility>
</ec:ChangePropertyAction.Value>
</ec:ChangePropertyAction>
</ec:DataTrigger>
</interactivity:Interaction.Triggers>
</Button>
Note Answered from phone syntax may not be correct

WP7: change the background of a border with converter

I'm going crazy with converters. I know that I must use it to change the "exit value" of my values, when needed, but I don't know how to use right for my case.
I have my simple MVVM (3 fields only) and my main window with a list of my items. The first item is calculated depending on a function, and can show YES or NOT, the other values are binded directly.
This is working well, but I need to change the background and foreground colors depending on the YES or NOT value I have in the first calculated field. For example:
YES (must be blue) - ITEM 1
NO (must be grey) - ITEM 2
YES (must be blue) - ITEM 3
While the internal values in my database are (in this case the calc is modulus):
2 - ITEM 1
3 - ITEM 2
4 - ITEM 3
My ListBox code is this:
<phone:PhoneApplicationPage
x:Class="Pasti.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="My App" Style="{StaticResource PhoneTextNormalStyle}" />
<TextBlock Text="My List" Style="{StaticResource PhoneTextTitle1Style}" />
</StackPanel>
<ListBox x:Name="lstPills" Grid.Row="1" ItemsSource="{Binding AllItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch" Width="440">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="90" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border Background="HERE MUST GO THE CONVERTER, I SUPOSE">
<TextBlock Text="{Binding IsPair, Mode=TwoWay}"/>
</Border>
<TextBlock
Text="{Binding Name}"
FontSize="{StaticResource PhoneFontSizeLarge}"
Grid.Column="1"
VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</phone:PhoneApplicationPage>
And the CS code is this for this page:
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
// Set the page DataContext property to the ViewModel.
this.DataContext = App.ViewModel;
}
}
For the calculated field, I added this to the Model (_myNumber holds the value I must check):
// Define a custom field based on some database values
// Get is calculated, while set will force it to refresh by Notifying
public string IsPair
{
get
{
return _myNumber % 2 == 0 ? "YES" : "NO";
}
set
{
NotifyPropertyChanged("IsPair");
}
}
NOTE: Because I don't know other way to force the list to refresh, I put the set property to only notify and the TwoWay Mode, and I just do a IsPair = "" when I want it to recalculate. If there are other way to do it, will be welcome.
So, with this info, how can I made a Converter that, based on my IsPair value, set the Background property of the Border to Blue or Grey? I saw a lot of Converter examples, but still don't get the point to do exactly this.
I suppose I must put something like this in the MainPage.cs, under the MainPage Class:
// Converter for the YES-NO column on the list
public class IsPairConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (MY_CALCULATED_VALUE == "YES")
return "Blue";
return "Grey";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
But how to get the MY_CALCULATED_VALUE, and how to set the converter in the Background value of the Border?
So close!
First, bind the background to IsPair and use the converter:
<Border Background="{Binding IsPair, Converter={StaticResource IsPairConverter}}">
<TextBlock Text="{Binding IsPair, Mode=TwoWay}"/>
</Border>
In your converter, create a brush depending on the value:
public class IsPairConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// You might want to add additional checks for type safety
var calculatedValue = (string)value;
var color = calculatedValue == "YES" ? Colors.Blue : Colors.Gray;
return new SolidColorBrush { Color = color };
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And you're done.
If you want the value to be computed only one time, instead of every time IsPair is called, you can do the computation in the setter of MyNumber and assign it to IsPair:
private int myNumber;
public string IsPair { get; protected set; }
protected int MyNumber
{
get
{
return this.myNumber;
}
set
{
this.myNumber = value;
this.IsPair = value % 2 == 0 ? "YES" : "NO";
this.NotifyPropertyChanged("IsPair");
}
}

Invoke IValueConverter's ConvertBack on load

I bind combobox (that is a part if listbox item template) to enum, the selected item is bound to the collection that is bound to listbox.
I use a converter for some logic.
The problem is that the ConvertBack is not invoked on startup, but only when I re-select the item in combobox.
I need it to invoke also on start.
public enum FullEnum
{
Apple,
Banana,
Pear
}
<Window.Resources>
<local:EnumConverter x:Key="enumConverter"/>
<ObjectDataProvider x:Key="DataT"
MethodName="GetValues"
ObjectType="{x:Type sys:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="local:FullEnum" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="190*" />
<RowDefinition Height="71*" />
</Grid.RowDefinitions>
<ListBox Name="list1" Margin="0,0,0,37">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=Label}"></TextBlock>
<ComboBox Height="23" Width="90"
ItemsSource="{Binding Source={StaticResource DataT}}"
SelectedValue="{Binding Path=Oped, Converter={StaticResource enumConverter}}">
</ComboBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
List<Item1> list = new List<Item1>();
public Window1()
{
InitializeComponent();
list.Add(new Item1 { Label="label1" });
list.Add(new Item1 { Label = "label2" });
list.Add(new Item1 { Label = "label3" });
list1.ItemsSource = list;
}
public class Item1
{
public FullEnum Oped { get; set; }
public string Label { get; set; }
}
public class EnumConverterr : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//some code
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if ((int)value != 0)
return (EnumSuperior)value;
return (EnumSuperior)7;
}
}
The return converter is not called by WPF on initialization because it has just gotten the initial values from the data context. The source and target of the data binding should have the same values so there is no reason to update the source.
You have not posted your convert back logic, but you must have some "state-ful" logic in the converter. Converters should be stateless (no side-effects, immutable). All of the conversion should be based on the value, a parameter, and converter properties that are not modified during the conversion.
If your converter is stateless, all you need to do is initialize the data source properly and you should no longer need that initial convert back call.

C# / WPF - DesignData - Binding to DesignData Collection Properties

I like design time data, especially when creating small widgets. For this very simple use case I'm having trouble binding to the properties of a design-time list which I have created in xaml.
Please find my ViewModel, View and SampleData below;
ViewModel
internal class SummaryViewModel : ViewModelBase
{
public string Title { get; set; }
public IList<Person> PersonList { get; set; }
internal SummaryViewModel()
{
PersonList = new List<Person>();
}
}
Sample Data
<ViewModel:SummaryViewModel xmlns:ViewModel="ViewModel" Title="Test Title">
<ViewModel:SummaryViewModel.Connections>
<ViewModel:ConnectionViewModel Id="0" />
<ViewModel:ConnectionViewModel Id="1" />
</ViewModel:SummaryViewModel.Connections>
</ViewModel:SummaryViewModel>
View
<StackPanel x:Class="View.SummaryView"
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"
mc:Ignorable="d"
d:DesignHeight="100"
d:DesignWidth="100"
d:DataContext="{d:DesignData Source=/DesignData/SampleSummaryViewModel.xaml}"
Orientation="Vertical"
Background="LightGreen">
<!-- This Works -->
<Label FontSize="10" FontWeight="Bold" Content="{Binding Title}" />
<!-- This Works -->
<ListBox ItemsSource="{Binding PersonList}" />
<!-- This DOESN'T work -->
<Label FontSize="8" Content="{Binding PersonList, Path=Count}"/>
</StackPanel>
How would you configure SampleData such that you could bind to the Count of a list specified therein?
I have tried setting the resource type as both DesignData and DesignDataWithDesignTimeCreatableTypes with no luck.
It should be:
<Label FontSize="8" Content="{Binding Path=PersonList.Count}"/>
Also MÃ¥rten is correct, you should use an ObservableCollection instead.
HTH
CityView, just as a side note: to debug DataBinding I usually use an empty converter which only returns the value it was given. I put a breakpoint in there and that way I can see what exactly is going back and forth.
public class BindTestConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
}
Combined with that and what the Output window tells me usually leads me to a solution to the problem at hand.
It should work, but become a one-time binding since your list does not implement INotifyPropertyChanged and therefore the binding is not updated when Count changes.
Try using an ObservableCollection<Person> instead.

Categories

Resources