How to pass CollectionView object to custom control in .NET Maui? - c#

I'm trying to leverage a custom control within a CollectionView and would like to pass the entire object of the particular CollectionView ItemTemplate into my custom control.
Here's my xaml page:
<CollectionView ItemsSource="{Binding WorkOps}" SelectionMode="None" ItemsLayout="VerticalList">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="75" />
<ColumnDefinition Width="15" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Label Grid.Column="0"
Text="{Binding OpType}"
FontSize="Caption"
VerticalTextAlignment="Center"/>
<Label Grid.Column="1"
Text="{Binding OpNumber}"
FontSize="Caption"
VerticalTextAlignment="Center"/>
<Label Grid.Column="2"
Text="{Binding Instructions}"
FontSize="Body"/>
<Entry Grid.Column="2"
Grid.Row="1"
Text="{Binding Measure}"
IsVisible="{Binding IsSimpleMeasure}" />
<root:TableMeasureView Grid.Column="2"
Grid.Row="1"
Op="{Binding .}"
IsVisible="{Binding IsTableMeasure}" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
and here is my custom control I'm trying to implement:
public class TableMeasureView : Grid
{
public static readonly BindableProperty WorkOpProperty =
BindableProperty.Create(nameof(Op), typeof(WorkOp), typeof(ContentPage));
public WorkOp Op
{
get { return (WorkOp)GetValue(WorkOpProperty); }
set { SetValue(WorkOpProperty, value); }
}
public TableMeasureView()
{
}
// ...
}
I get the following message when trying to build:
XamlC error XFC0009: No property, BindableProperty, or event found for "Op", or mismatching type between value and property.
Is what I'm attempting possible?

Yes it is possible. What's happening is that the xaml doesn't attempt to figure out that the type of {Binding .} is WorkOp. It wants a property of type object.
The fix is to give it a property of type object. Then for convenient access in your custom control, make a second property that casts that to WorkOp:
public static readonly BindableProperty WorkOpProperty =
BindableProperty.Create(nameof(Op), typeof(object), typeof(ContentPage));
public object Op
{
get { return GetValue(WorkOpProperty); }
set { SetValue(WorkOpProperty, value); }
}
private WorkOp TypedOp => (WorkOp)Op;
NOTE: Change Op and TypedOp above to whatever names you like. If you change Op, remember to also change in the xaml that refers to it.

Related

Binding Stepper value to the corresponding listview item's parameter - Xamarin.forms

I put the stepper on listview so each record has one stepper. I would like to bind the stepper onchanged value to one of the corresponding objects parameter value. So each listed records' parameter can be altered by corresponding stepper.
Is it possible to do so?
Here is the ListView:
<ListView x:Name="TempMenuListView" HasUnevenRows="true" Grid.Row="2" SeparatorColor="Black" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Text="{Binding PiattoTipo}" Grid.Row="0" Grid.Column="0" TextColor="Black" />
<Label Text="{Binding Piatto}" Grid.Row="0" Grid.Column="1" TextColor="Black" />
<Label x:Name="numeroPiattiLabel" Text="{Binding NumeroPiatti}" Grid.Row="0" Grid.Column="2" TextColor="Black" />
<Stepper x:Name="stepper" Maximum="20" ValueChanged="OnStepperValueChanged"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Here is the class for ListView Data:
[Table("TempMenu")]
public class TempMenu
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public DateTime Date { get; set; }
public int TavoloNo { get; set; }
public string PiattoTipo { get; set; }
public string Piatto { get; set; }
public int NumeroPiatti { get; set; }
}
An empty OnStepperValueChanged method:
private void OnStepperValueChanged(object sender, ValueChangedEventArgs e)
{
}
I need the stepper to change respectable NumeroPiatti value.
First of all I would suggest you to do separation of your Entity (Table class) and models which you will use to show some content in your pages. It leads to writing more code but it is cleaner and nicer to maintain, especially if you want to implement INotifyPropertyChanged in this models.
If you want to follow the MVVM, make sure that your model implements INotifyPropertyChanged you can also make some base model or use Fody to remove some boilerplate code.
Rest of it is same as you mentioned in your last comment you can use Stepper control in this way:
//... rest of your code XAML
<Stepper x:Name="stepper" Maximum="20" Value={Binding SomePropertyInModel} />
//... rest of your code XAML
Wishing you lots of luck with coding!

puting combobox inside a grid makes selected item empty

I have two comboboxes that I would to show besides each other.
I am using a grid, and two columns for this... but when I do this, the initialliy selected item for the comboxbox disappears
so, if I put them in a grid... i get this:
If I remove the grid... the combobox gets the initial value...
the xaml looks like this... here with the grid part commented out... I just don't get why adding/removing the grid makes a difference...
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<StackPanel>
<TextBlock Text="{x:Bind ViewModel.LoadErrorMessage, Mode=OneWay}" />
<!--<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="5*"/>
</Grid.ColumnDefinitions>-->
<ComboBox HorizontalAlignment="Stretch" ItemsSource="{x:Bind ViewModel.WeaponCountRange}" SelectedItem="{x:Bind ViewModel.WeaponCount, Mode=TwoWay}"></ComboBox>
<ComboBox Grid.Column="1" HorizontalAlignment="Stretch" ItemsSource="{x:Bind ViewModel.Weapons}" SelectedItem="{x:Bind ViewModel.SelectedWeapon, Mode=TwoWay}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<Border Background="Black">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ImageFile}" Stretch="Uniform" Height="48"></Image>
<TextBlock Foreground="Yellow" Height="48" VerticalAlignment="Stretch" Text="{Binding Name}"></TextBlock>
<Image VerticalAlignment="Top" Visibility="{Binding ShieldPiercingVis}" Height="12" Source="/Assets/ship_modules/dragon_missile.png"/>
</StackPanel>
</Border>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<!--</Grid>-->
<Border>
the code behind that populates, is a async task... see the following
public ObservableCollection<WeaponViewModel> Weapons = new ObservableCollection<WeaponViewModel>();
private WeaponViewModel _selectedWeapon;
public WeaponViewModel SelectedWeapon
{
get => _selectedWeapon;
set => SetProperty(ref _selectedWeapon, value);
}
private async Task Initialize()
{
{
var wRepo = new WeaponRepository();
await wRepo.Initialize();
foreach (var item in wRepo.Weapons)
{
Weapons.Add(new WeaponViewModel(item));
if (Weapons.Count == 1)
SelectedWeapon = Weapons[0];
}
}
...
I could not reproduce your issue. When I run your code, it throw exception. And I found that you have not set IValueConverter for SelectedItem. I have created the converter for SelectedItem and it works well in my side. I will upload the code sample that you could refer to.
Converter
public class Converter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return value as ComboBoxItem;
}
}

Command binding in external itemtemplate of xamarin.forms listview?

Im using mvvm and viewmodel locator.Im using button commands or listview itemtap behaviors with no problem.But in one of my page I need to use external itemtemplate (resources).In this template i can bind labels with no problem.But I cant Bind command of button,I get this error "Can't resolve name on Element" .
here is the external custom cell
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:core="clr-namespace:paux;assembly=paux"
xmlns:controls="clr-namespace:paux.Controls;assembly=paux"
xmlns:xlabs="clr-namespace:XLabs.Forms.Controls;assembly=XLabs.Forms"
xmlns:base="clr-namespace:paux.Base;assembly=paux"
x:Class="paux.Controls.Cells.CustomDonemCell"> <ViewCell.View>
<Grid
BackgroundColor="{StaticResource WhiteColor}" Margin="0,0,0,0">
<Grid Grid.Column="1" Grid.Row="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackLayout
Grid.Column="1"
Margin="0,16,0,0"
Orientation="Vertical"
Spacing="0"
VerticalOptions="Start"> -
<Button Command="{Binding Path=BindingContext.mybuttonClicked, Source={x:Reference Name=mylistView}}" CommandParameter="{Binding id}" Text="My Button"/>
<controls:MultiLineLabel Text="{Binding BolumAdi}" Lines="2" VerticalOptions="Center" HorizontalOptions="Center" LineBreakMode="TailTruncation"
Margin="0,0,0,3"/>
</StackLayout>
</Grid>
</Grid>
</Grid> </ViewCell.View> </ViewCell>
This is the Page ,with templateselector (its working fine)
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:behavior="clr-namespace:paux.Behavior;assembly=paux"
xmlns:animations="clr-namespace:paux.Animations;assembly=paux"
xmlns:triggers="clr-namespace:paux.Triggers;assembly=paux"
xmlns:effects="clr-namespace:paux.Effects;assembly=paux"
xmlns:templateSelectors="clr-namespace:paux.TemplateSelectors;assembly=paux"
xmlns:converters="clr-namespace:paux.Converters;assembly=paux"
x:Class="paux.Pages.PageOgrenciDonem" >
<ContentPage.Resources>
<ResourceDictionary>
<templateSelectors:DataTemplateSelector x:Key="ogrenciDonemTemplate" />
</ResourceDictionary>
</ContentPage.Resources>
<Grid>
<ListView
x:Name="mylistView"
CachingStrategy="RecycleElement"
ItemsSource="{Binding OgrencilikList, Mode=OneWay}"
HasUnevenRows="True"
SeparatorVisibility="None"
ItemTemplate="{StaticResource ogrenciDonemTemplate}" >
<ListView.Margin>
<OnPlatform x:TypeArguments="Thickness"
Android="8"
WinPhone="8"/>
</ListView.Margin>
</ListView>
</Grid>
</ContentPage>
and in the viewmodel
public static readonly BindableProperty TestCommandProperty =
BindableProperty.Create("TestCommand", typeof(ICommand), typeof(CustomDonemCell), null);
public ICommand TestCommand => new Command<detay>(testclickevent);
private async void testclickevent(detay item)
{ await NavigationService.NavigateToAsync<detayviewmodel(item.id.ToString());
}
The problem you have is where to define your handler. You defined it in the PageTestViewModel but in xaml this command is not defined for the list or page model. It is defined for the ITEM. So, you have 3 options. You don't have to define both OnButtonClicked and TestCommand, I just show it as an option. If you define both you will get called in 2 places (see below)
<Button Clicked="OnButtonClicked" Command="{Binding TestCommand}" CommandParameter="{Binding id}" Text="My Button"/>
To be called via OnButtonClicked
public partial class CustomCell : ViewCell
{
public CustomCell()
{
InitializeComponent();
}
void OnButtonClicked(object sender, EventArgs args)
{
}
}
To be called in item
public class testdata
{
public string id { get; set; }
public Command TestCommand
{
get
{
return new Command((o) =>
{
System.Diagnostics.Debug.WriteLine("Item " + o.ToString());
});
}
}
}
To be called in PageTestViewModel where you wanted to be called you need to specify path to your model. This is more complicated. Send me a message if previous 2 methods don't work for you. But this is going to be tricky as you have your ViewCell xaml in separate file, so you cannot access page name or list name. I am not sure if it is good design to specify handler in cell which leaves in the top-level model. You might want to use one of 2 handlers I suggested and subscribe for events which will be fired from those handlers up.

How to do DataBinding for ControlTemplates in Xamarin.Forms

Disclaimer: I'm new to Xamarin.Forms and Xaml in general
I'm trying to figure out how DataBinding works within templates in Xamarin.Forms.
Xaml:
<ContentPage.Resources>
<ResourceDictionary>
<ControlTemplate x:Key="GridItem">
<Grid HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" Padding="0" ColumnSpacing="0" RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<ContentView Grid.Row="0" Grid.Column="0" BackgroundColor="Red" VerticalOptions="FillAndExpand" Padding="15">
<Image Source="{TemplateBinding ImageSource}" BackgroundColor="Red" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" />
</ContentView>
<Label Grid.Row="1" Grid.Column="0" Text="{TemplateBinding LabelText}" FontSize="Large" TextColor="White" BackgroundColor="#8B0000" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" />
</Grid>
</ControlTemplate>
</ResourceDictionary>
</ContentPage.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<ContentView x:Name="randomButton1" ControlTemplate="{StaticResource GridItem}" Grid.Row="0" Grid.Column="0" />
<ContentView x:Name="randomButton2" ControlTemplate="{StaticResource GridItem}" Grid.Row="0" Grid.Column="1" />
</Grid>
C# Code behind
using Xamarin.Forms;
namespace TestApp.XForms
{
public partial class Page1 : ContentPage
{
private readonly MainButtonViewModel[] buttons;
public Page1()
{
InitializeComponent();
randomButton1.BindingContext = new MainButtonViewModel("some text1", "someimage1.png");
randomButton2.BindingContext = new MainButtonViewModel("some text2", "someimage2.png");
}
}
public class MainButtonViewModel : BindableObject
{
private string labelText;
public string LabelText
{
get { return labelText; }
set
{
labelText = value;
OnPropertyChanged();
}
}
private string imageSource;
public string ImageSource
{
get { return imageSource; }
set
{
imageSource = value;
OnPropertyChanged();
}
}
public MainButtonViewModel()
{
}
public MainButtonViewModel(string text, string imageLocation)
{
LabelText = text;
ImageSource = imageLocation;
}
}
}
So my templates seem to work. I see 2 red blocks. But none of the bindings seem to have worked. Everything is empty:
How can I get the data binding to work?
Your view model doesn't support INotifyPropertyChanged. You could use Xamarin.Forms class:
public class SomeViewModel : BindableObject
And then you could notify UI, that some property has been changed:
private string _someStringProperty;
public string SomeStringProperty
{
get { return _someStringProperty; }
set
{
_someStringProperty = value;
OnPropertyChanged();
}
}
Also will be helpful Binding from ControlTemplate.
When you use ControlTemplate it requires TemplateBinding for BindingContext, otherwise it won't have any BindingContext itself. You can use DataTemplate without setting BindingContext, it inherits parent's BindingContext itself.
i.e:
<ControlTemplate x:Key="yourTemplate" BindingContext="{TemplateBinding BindingContext}">
<Grid>
<!--Your Template-->
</Grid>
</ControlTemplate>
<DataTemplate x:Key="yourTemplate">
<Grid>
<!--Your Template-->
</Grid>
</DataTemplate>

How do I get WPF Grid columns defined with star to clip content?

I have a Grid control that is proportioned using star e.g.
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50*" />
<ColumnDefinition Width="100*" />
<ColumnDefinition Width="50*" />
</Grid.ColumnDefinitions>
However putting a long TextBlock in the grid that overflows causes the proportions to be upset. e.g.
<TextBlock Text="Foo" Grid.Column="0" />
<TextBlock Text="Some long text here which overflows" Grid.Column="1" />
<TextBlock Text="Foo" Grid.Column="2" />
This causes the central column to be more than double the other two. How do I maintain the specified proportions? Is it possible to clip the content?
I have set TextTrimming="CharacterEllipsis" on the TextBlocks but no luck.
Edit
Crucially it seems, the Grid is inside a DataTemplate, paste the following to observe the behaviour,
<!-- FallbackValue is just a quick hack to get some rows to show at design-time -->
<ListBox ItemsSource="{Binding Foo, FallbackValue=1234}"
HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50*" />
<ColumnDefinition Width="100*" />
<ColumnDefinition Width="50*" />
</Grid.ColumnDefinitions>
<TextBlock Text="Foo" Grid.Column="0" />
<TextBlock Text="Some long text here which overflows" TextTrimming="CharacterEllipsis" Grid.Column="1" />
<TextBlock Text="Foo" Grid.Column="2" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The reason why this is important is that I have another Grid as a sibling of the ListBox which displays the 'headers' for the columns shown in the ListBox as follows,
<Grid>
... Headers and column definitions here
</Grid>
<ListBox ...>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
... Matching column definitions here
</Grid>
</DateTemplate>
</ListBox.ItemTemplate>
</ListBox>
and so it is important that the columns match up.
I have tried to bind the ColumnDefinitions inside the DataTemplate to the external Grid ColumnDefinitions but I cannot get easily a binding reference to it.
This is one of the most annoying problems with WPF. Since the available space yielded to the templated grid is infinite, the actual content will take as much space as it wants.
The simplest way is to fix a certain width to the Grid, but that solves only the situations where there's no resizing.
Whereas you want to stretch the ListBox size (width, in the specific), unfortunately I guess that there's no any better solution other than a custom converter.
Here is my solution:
<Window.Resources>
<local:MyConv x:Key="cv1" />
</Window.Resources>
<Grid>
<ListBox
ItemsSource="{Binding Foo, FallbackValue=1234}"
HorizontalContentAlignment="Stretch"
>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Width="{Binding Path=ActualWidth, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListBox}, Converter={StaticResource cv1}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50*" />
<ColumnDefinition Width="100*" />
<ColumnDefinition Width="50*" />
</Grid.ColumnDefinitions>
<TextBlock Text="Foo" Grid.Column="0" />
<TextBlock Text="Some long text here which overflows" TextTrimming="CharacterEllipsis" Grid.Column="1" />
<TextBlock Text="Foo" Grid.Column="2" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
And the converter:
class MyConv : IValueConverter
{
public object Convert(
object value,
Type targetType,
object parameter,
System.Globalization.CultureInfo culture
)
{
return (double)value - 30.0;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Even though this is an old post I'm adding my findings as they might be relevant for other people reading this post.
I had a similar issue (my * columns weren't dividing the width evenly as expected anymore, they were just sizing based on the content).
The root cause here was that I had a ListView with an ItemsSource linked to a List. The ListView in WPF contains a ScrollViewer and a ScrollViewer doesn't have a fixed width.
Without a fixed width a Grid can't properly determine what width to give to a * column and switches to a different sizing method.
Solution
I now use an ItemsControl which doesn't contain a ScrollViewer and thus the Width is known allowing the Grid to properly size it's columns.
For more details on how exactly the Grid handles it's sizing I suggest you decompile the Grid class and have a look at the following method:
protected override Size MeasureOverride(Size constraint)
This is my MainWindow.xaml from my test application (comment out the ListView to see the difference in behaviour):
<Window x:Class="WPFSO.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfso="clr-namespace:WPFSO"
Title="MainWindow" Height="150" Width="525">
<Window.DataContext>
<wpfso:SharedSizeScopeViewModel />
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type wpfso:TestViewModel}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" x:Name="SecondColumn" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" x:Name="FourthColumn" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Name}" />
<TextBlock Grid.Column="1" Background="LightGray" Text="{Binding Name2}"/>
<TextBlock Grid.Column="2" Text="{Binding Name3}"/>
<TextBlock Grid.Column="3" Background="Orange" Text="{Binding Name4}"/>
<!--<TextBlock Grid.Column="1" Background="Blue" HorizontalAlignment="Stretch" />
<TextBlock Grid.Column="3" Background="Orange" HorizontalAlignment="Stretch" />-->
</Grid>
</DataTemplate>
<DataTemplate x:Key="MainDataTemplate" DataType="wpfso:SharedSizeScopeViewModel" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<CheckBox Grid.Row="0" Grid.ColumnSpan="4" HorizontalAlignment="Left" FlowDirection="RightToLeft" Margin="0,0,0,25">
<TextBlock FlowDirection="LeftToRight" Text="Show differences" Style="{StaticResource LabelStyle}" />
</CheckBox>
<TextBlock Grid.Row="1" Grid.Column="0" Text="PropertyName" Style="{StaticResource LabelStyle}" />
<TextBlock Grid.Row="1" Grid.Column="1" Text="Previous value" Style="{StaticResource LabelStyle}" />
<TextBlock Grid.Row="1" Grid.Column="3" Text="Current value" Style="{StaticResource LabelStyle}" />
<ListView Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="4" ItemsSource="{Binding Entries}" HorizontalAlignment="Stretch" Margin="0" HorizontalContentAlignment="Stretch"/>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid Name="RootGrid">
<ItemsControl ItemsSource="{Binding Entries}" />
<!--<ListView ItemsSource="{Binding Entries}" />-->
</Grid>
</Window>
The ViewModels used during this test:
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WPFSO
{
public class SharedSizeScopeViewModel : INotifyPropertyChanged
{
public SharedSizeScopeViewModel()
{
var testEntries = new ObservableCollection<TestViewModel>();
testEntries.Add(new TestViewModel
{
Name = "Test",
Name2 = "Looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong test",
Name3 = "Short test",
Name4 = "Nothing"
});
Entries = testEntries;
}
private ObservableCollection<TestViewModel> _entries;
public ObservableCollection<TestViewModel> Entries
{
get { return _entries; }
set
{
_entries = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
First viewmodel
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WPFSO
{
public class SharedSizeScopeViewModel : INotifyPropertyChanged
{
public SharedSizeScopeViewModel()
{
var testEntries = new ObservableCollection<TestViewModel>();
testEntries.Add(new TestViewModel
{
Name = "Test",
Name2 = "Looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong test",
Name3 = "Short test",
Name4 = "Nothing"
});
Entries = testEntries;
}
private ObservableCollection<TestViewModel> _entries;
public ObservableCollection<TestViewModel> Entries
{
get { return _entries; }
set
{
_entries = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Second viewmodel
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WPFSO
{
public class TestViewModel : INotifyPropertyChanged
{
private string _name;
private string _name2;
private string _name3;
private string _name4;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged();
}
}
public string Name2
{
get { return _name2; }
set
{
_name2 = value;
OnPropertyChanged();
}
}
public string Name3
{
get { return _name3; }
set
{
_name3 = value;
OnPropertyChanged();
}
}
public string Name4
{
get { return _name4; }
set
{
_name4 = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Set
TextTrimming="CharacterEllipsis"
on the TextBlock.
It works for me. As you have defined the middle column should be twice the size of the other.
I find myself in a similar situation but TextTrimming isn't available.
Ends up binding child Width to Grid.ActualWidth with a Converter converts the ratio into absolute width.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50*" />
<ColumnDefinition Width="100*" />
<ColumnDefinition Width="50*" />
</Grid.ColumnDefinitions>
<Grid.Resources>
<local:PartConv x:Key="partConv"/>
<sys:Double x:Key="r0">0.25</sys:Double>
<sys:Double x:Key="r1">0.5</sys:Double>
<sys:Double x:Key="r2">0.25</sys:Double>
</Grid.Resources>
<TextBlock Text="Foo" Grid.Column="0"
Width="{Binding ActualWidth,
RelativeSource={RelativeSource AncestorType=Grid}},
Converter={StaticResource partConv},
ConverterParameter={StaticResource r0}}"/>
<TextBlock Text="Some long text here which overflows" Grid.Column="1"
Width="{Binding ActualWidth,
RelativeSource={RelativeSource AncestorType=Grid}},
Converter={StaticResource partConv},
ConverterParameter={StaticResource r1}}"/>
<TextBlock Text="Foo" Grid.Column="2"
Width="{Binding ActualWidth,
RelativeSource={RelativeSource AncestorType=Grid}},
Converter={StaticResource partConv},
ConverterParameter={StaticResource r2}}"/>
</Grid>
[ValueConversion(typeof(double), typeof(double))]
public class PartConv : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
=> ((double)value) * ((double)parameter);
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> ((double)value) / ((double)parameter);
}

Categories