Sorry another beginner question.
I am trying to include an example from the Avalonia Documentation into my test project but I am getting stuck on trying to use MainWindowViewModel as well as code-behind for MainWindow (MainWindow.axaml.cs)
The example is the last one on which is unfortunately a little out of date.
<StackPanel Margin="20">
<ComboBox x:Name="fontComboBox" SelectedIndex="0"
Width="200" MaxDropDownHeight="300">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" FontFamily="{Binding}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
using Avalonia.Controls;
using Avalonia.Media;
using System.Linq;
namespace AvaloniaControls.Views
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
fontComboBox.Items = FontManager.Current
.GetInstalledFontFamilyNames()
.Select(x => new FontFamily(x))
.OrderBy(x=>x.Name);
fontComboBox.SelectedIndex = 0;
}
}
}
The MainWindow has several other controls using MVVM view model
x:DataType="vm:MainWindowViewModel"
so whenever I type {Binding } rider helpfully reminds me with '(MainWindowViewModel). Path=' I have also learnt that when you use the term 'Binding' it must be to a Property - unless, perhaps, this is in the Code Behind. (Which is just doing my head in when trying to populate, in this case, a ComboBox.)
I think I have sucessfully changed the code-behind to... (at least in debug lFonts contains the data I expect)
public List<string> lFonts = new List<string>();
public MainWindow()
{
InitializeComponent();
ColourComboBox.ItemsSource = ColourBasesTxt;
ThemesComboBox.ItemsSource = Themes;
IFontCollection allFonts = FontManager.Current.SystemFonts;
foreach (var font in allFonts)
{
lFonts.Add(font.Name);
}
FontComboBox.ItemsSource = lFonts;
Now what I cant workout is how to link this to the xaml. The example code had
<ComboBox x:Name="fontComboBox" SelectedIndex="0"
Width="200" MaxDropDownHeight="300">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" FontFamily="{Binding}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
in my code
<ComboBox x:Name="FontComboBox" Grid.Column="1" Grid.Row="3"
HorizontalAlignment="Left" HorizontalContentAlignment="Left" VerticalAlignment="Center"
MaxDropDownHeight="300" Width="150" Height="40" FontSize="16"
SelectedItem="{ Binding SelectedFont}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Now
<TextBlock Text="{Binding Name}" />
does not work because its trying to bind to MainWindowViewModel.Name
I have tried many crazy suggestions to get the data context within the DataTemplate to be the code-behind.
All but 1 did not pass the syntax check.
I cant refocus the whole ComboBox because the 'selectedItem' needs to be to a property in MainWindowViewModel (which it is).
The one that got close is
<TextBlock Text="{Binding ElementName=FontComboBox}" />
That actually ran but the ComboBox is filled with text 'Avalonia.Controls.ComboBox' line after line.
I am sorry I am so new to this method I even lack the language to formulate the question properly. Perhaps it is "For a list type control, how can you code it so as to have the 'source' (code supplying the list content) in one module and the 'SelectedItem' in a different module."
or maybe " how can you specify a DataContext to be the code behind module for any given xaml"
Thanks for your patience JC
Adding Full code for a cut down version. Any auto generated code has been left in
MainWindow.axaml
<Window xmlns=";
xmlns:x=";
xmlns:vm="using:Colour_Font_experiment3v1_snip.ViewModels"
xmlns:d=";
xmlns:mc=";
xmlns:views="clr-namespace:Colour_Font_experiment3v1_snip.Views"
mc:Ignorable="d" d:DesignWidth="900" d:DesignHeight="450"
Width="900" Height="450"
x:Class="Colour_Font_experiment3v1_snip.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
Icon="/Assets/avalonia-logo.ico"
Title="Colour_Font_experiment3v1_snip">
<Design.DataContext>
<!-- This only sets the DataContext for the previewer in an IDE,
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
<vm:MainWindowViewModel/>
</Design.DataContext>
<StackPanel Orientation="Horizontal" Spacing="10" Background="#E6F5FC" >
<Border HorizontalAlignment="Left"
BorderBrush="Gray" BorderThickness="1"
CornerRadius="5" Padding="15 3">
<StackPanel Orientation="Vertical" Spacing="10">
<TextBlock Text="{Binding DummyPlaceHolder}" HorizontalAlignment="Left" VerticalAlignment="Center" Width="200" FontStyle="Italic"/>
<Grid ColumnDefinitions="*, 3*" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto" Margin="4"
ShowGridLines ="True">
<Label Content="" Grid.Column="0" Grid.Row="0" Height="1" />
<Label Content="Theme :" Grid.Column="0" Grid.Row="1" Height="50"/>
<Label Content="Font :" Grid.Column="0" Grid.Row="2" Height="50" />
<Label Content="Start Folder:" Grid.Column="0" Grid.Row="3" Height="50" />
<Label Content="" Grid.Column="0" Grid.Row="4" Height="50" />
<!-- see MainWindow.axaml.cs for ItemsSource ==> ThemesCombobox.ItemsSource = Themes; -->
<ComboBox x:Name="ThemesComboBox" Grid.Column="1" Grid.Row="1"
SelectedItem="{Binding SelectedTheme}"
Width="150" Height="40" >
</ComboBox>
<!-- and for use of textblock within ComboBox -->
<ComboBox x:Name="FontComboBox" Grid.Column="1" Grid.Row="2"
MaxDropDownHeight="300" Width="150" Height="40"
SelectedItem="{ Binding SelectedFont}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="xxxx" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBox Grid.Column="1" Grid.Row="3"
Text="{Binding StartFolder}"
Padding="10,8"
Width="400" Height="40" />
<Button Grid.Column="1" Grid.Row="4"
Command="{Binding SaveButtonClick}"
Content="Set / Save"
Height="40" Width="100" />
</Grid>
</StackPanel>
</Border>
</StackPanel>
</Window>
MainWindow.axaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Controls;
using Avalonia.Fonts.Inter;
using Avalonia.Media;
using Avalonia.Media.Fonts;
namespace Colour_Font_experiment3v1_snip.Views;
public partial class MainWindow : Window
{
private string[] Themes = new string[] { "Dark", "Light" };
private List<string> lFonts = new List<string>();
public MainWindow()
{
InitializeComponent();
ThemesComboBox.ItemsSource = Themes;
IFontCollection allFonts = FontManager.Current.SystemFonts;
foreach (var font in allFonts)
{
Console.WriteLine(font.Name);
lFonts.Add(font.Name);
}
Console.WriteLine($"{lFonts.Count} font Names loaded");
FontComboBox.ItemsSource = lFonts;
FontComboBox.SelectedIndex = 0;
}
}
MainWindowViewModel
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reactive;
using Avalonia;
using Avalonia.Interactivity;
using ReactiveUI;
using Romzetron.Avalonia;
namespace Colour_Font_experiment3v1_snip.ViewModels;
public class MainWindowViewModel : ViewModelBase
{
// *****************************************************************
private string _dummyPlaceHolder = "Under Construction!";
public string DummyPlaceHolder
{
get => _dummyPlaceHolder;
set => this.RaiseAndSetIfChanged(ref _dummyPlaceHolder, value);
}
public ReactiveCommand<Unit, Unit> SaveButtonClick { get; }
private string _selectedTheme = "Light";
public string SelectedTheme
{
get => _selectedTheme;
set => this.RaiseAndSetIfChanged(ref _selectedTheme, value);
}
private string _selectedFont;
public string SelectedFont
{
get => _selectedFont;
set => this.RaiseAndSetIfChanged(ref _selectedFont, value);
}
private string _startFolder = @"M:\Museum\ManagedCatalog";
public string StartFolder
{
get => _startFolder;
set => this.RaiseAndSetIfChanged(ref _startFolder, value);
}
// **********************************************************
// Constructor
public MainWindowViewModel()
{
SaveButtonClick = ReactiveCommand.Create(PerformSaveAction);
DummyPlaceHolder = "Current Colour Base is : 'redacted' " ;
}
private void PerformSaveAction()
{
Console.WriteLine("The \'Set / Save\' button was pressed.");
}
//*******************************************
private ColorTheme getCurrentTheme()
{
return GetRomzetronAvaloniaTheme().ColorTheme;
}
// **********************************************************
// JC added from .Avalonia?tab=readme-ov-file
private static RomzetronTheme? GetRomzetronAvaloniaTheme()
{
// Get a reference to the current application.
if (Application.Current is not App app)
return null;
// Loop through the styles in the application.
foreach (var style in app.Styles)
{
// Return RomzetronTheme if it is found.
if (style is RomzetronTheme theme)
return theme;
}
return null;
}
}
And just to refocus. The issue is to get the xaml line
<TextBlock Text="xxxx" />
actually connected to lFonts
in the code behind
Thanks again
JC
Sorry another beginner question.
I am trying to include an example from the Avalonia Documentation into my test project but I am getting stuck on trying to use MainWindowViewModel as well as code-behind for MainWindow (MainWindow.axaml.cs)
The example is the last one on https://docs.avaloniaui/docs/reference/controls/combobox which is unfortunately a little out of date.
<StackPanel Margin="20">
<ComboBox x:Name="fontComboBox" SelectedIndex="0"
Width="200" MaxDropDownHeight="300">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" FontFamily="{Binding}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
using Avalonia.Controls;
using Avalonia.Media;
using System.Linq;
namespace AvaloniaControls.Views
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
fontComboBox.Items = FontManager.Current
.GetInstalledFontFamilyNames()
.Select(x => new FontFamily(x))
.OrderBy(x=>x.Name);
fontComboBox.SelectedIndex = 0;
}
}
}
The MainWindow has several other controls using MVVM view model
x:DataType="vm:MainWindowViewModel"
so whenever I type {Binding } rider helpfully reminds me with '(MainWindowViewModel). Path=' I have also learnt that when you use the term 'Binding' it must be to a Property - unless, perhaps, this is in the Code Behind. (Which is just doing my head in when trying to populate, in this case, a ComboBox.)
I think I have sucessfully changed the code-behind to... (at least in debug lFonts contains the data I expect)
public List<string> lFonts = new List<string>();
public MainWindow()
{
InitializeComponent();
ColourComboBox.ItemsSource = ColourBasesTxt;
ThemesComboBox.ItemsSource = Themes;
IFontCollection allFonts = FontManager.Current.SystemFonts;
foreach (var font in allFonts)
{
lFonts.Add(font.Name);
}
FontComboBox.ItemsSource = lFonts;
Now what I cant workout is how to link this to the xaml. The example code had
<ComboBox x:Name="fontComboBox" SelectedIndex="0"
Width="200" MaxDropDownHeight="300">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" FontFamily="{Binding}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
in my code
<ComboBox x:Name="FontComboBox" Grid.Column="1" Grid.Row="3"
HorizontalAlignment="Left" HorizontalContentAlignment="Left" VerticalAlignment="Center"
MaxDropDownHeight="300" Width="150" Height="40" FontSize="16"
SelectedItem="{ Binding SelectedFont}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Now
<TextBlock Text="{Binding Name}" />
does not work because its trying to bind to MainWindowViewModel.Name
I have tried many crazy suggestions to get the data context within the DataTemplate to be the code-behind.
All but 1 did not pass the syntax check.
I cant refocus the whole ComboBox because the 'selectedItem' needs to be to a property in MainWindowViewModel (which it is).
The one that got close is
<TextBlock Text="{Binding ElementName=FontComboBox}" />
That actually ran but the ComboBox is filled with text 'Avalonia.Controls.ComboBox' line after line.
I am sorry I am so new to this method I even lack the language to formulate the question properly. Perhaps it is "For a list type control, how can you code it so as to have the 'source' (code supplying the list content) in one module and the 'SelectedItem' in a different module."
or maybe " how can you specify a DataContext to be the code behind module for any given xaml"
Thanks for your patience JC
Adding Full code for a cut down version. Any auto generated code has been left in
MainWindow.axaml
<Window xmlns="https://github/avaloniaui"
xmlns:x="http://schemas.microsoft/winfx/2006/xaml"
xmlns:vm="using:Colour_Font_experiment3v1_snip.ViewModels"
xmlns:d="http://schemas.microsoft/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats./markup-compatibility/2006"
xmlns:views="clr-namespace:Colour_Font_experiment3v1_snip.Views"
mc:Ignorable="d" d:DesignWidth="900" d:DesignHeight="450"
Width="900" Height="450"
x:Class="Colour_Font_experiment3v1_snip.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
Icon="/Assets/avalonia-logo.ico"
Title="Colour_Font_experiment3v1_snip">
<Design.DataContext>
<!-- This only sets the DataContext for the previewer in an IDE,
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
<vm:MainWindowViewModel/>
</Design.DataContext>
<StackPanel Orientation="Horizontal" Spacing="10" Background="#E6F5FC" >
<Border HorizontalAlignment="Left"
BorderBrush="Gray" BorderThickness="1"
CornerRadius="5" Padding="15 3">
<StackPanel Orientation="Vertical" Spacing="10">
<TextBlock Text="{Binding DummyPlaceHolder}" HorizontalAlignment="Left" VerticalAlignment="Center" Width="200" FontStyle="Italic"/>
<Grid ColumnDefinitions="*, 3*" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto" Margin="4"
ShowGridLines ="True">
<Label Content="" Grid.Column="0" Grid.Row="0" Height="1" />
<Label Content="Theme :" Grid.Column="0" Grid.Row="1" Height="50"/>
<Label Content="Font :" Grid.Column="0" Grid.Row="2" Height="50" />
<Label Content="Start Folder:" Grid.Column="0" Grid.Row="3" Height="50" />
<Label Content="" Grid.Column="0" Grid.Row="4" Height="50" />
<!-- see MainWindow.axaml.cs for ItemsSource ==> ThemesCombobox.ItemsSource = Themes; -->
<ComboBox x:Name="ThemesComboBox" Grid.Column="1" Grid.Row="1"
SelectedItem="{Binding SelectedTheme}"
Width="150" Height="40" >
</ComboBox>
<!-- and https://docs.avaloniaui/docs/reference/controls/combobox for use of textblock within ComboBox -->
<ComboBox x:Name="FontComboBox" Grid.Column="1" Grid.Row="2"
MaxDropDownHeight="300" Width="150" Height="40"
SelectedItem="{ Binding SelectedFont}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="xxxx" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBox Grid.Column="1" Grid.Row="3"
Text="{Binding StartFolder}"
Padding="10,8"
Width="400" Height="40" />
<Button Grid.Column="1" Grid.Row="4"
Command="{Binding SaveButtonClick}"
Content="Set / Save"
Height="40" Width="100" />
</Grid>
</StackPanel>
</Border>
</StackPanel>
</Window>
MainWindow.axaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Controls;
using Avalonia.Fonts.Inter;
using Avalonia.Media;
using Avalonia.Media.Fonts;
namespace Colour_Font_experiment3v1_snip.Views;
public partial class MainWindow : Window
{
private string[] Themes = new string[] { "Dark", "Light" };
private List<string> lFonts = new List<string>();
public MainWindow()
{
InitializeComponent();
ThemesComboBox.ItemsSource = Themes;
IFontCollection allFonts = FontManager.Current.SystemFonts;
foreach (var font in allFonts)
{
Console.WriteLine(font.Name);
lFonts.Add(font.Name);
}
Console.WriteLine($"{lFonts.Count} font Names loaded");
FontComboBox.ItemsSource = lFonts;
FontComboBox.SelectedIndex = 0;
}
}
MainWindowViewModel
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reactive;
using Avalonia;
using Avalonia.Interactivity;
using ReactiveUI;
using Romzetron.Avalonia;
namespace Colour_Font_experiment3v1_snip.ViewModels;
public class MainWindowViewModel : ViewModelBase
{
// *****************************************************************
private string _dummyPlaceHolder = "Under Construction!";
public string DummyPlaceHolder
{
get => _dummyPlaceHolder;
set => this.RaiseAndSetIfChanged(ref _dummyPlaceHolder, value);
}
public ReactiveCommand<Unit, Unit> SaveButtonClick { get; }
private string _selectedTheme = "Light";
public string SelectedTheme
{
get => _selectedTheme;
set => this.RaiseAndSetIfChanged(ref _selectedTheme, value);
}
private string _selectedFont;
public string SelectedFont
{
get => _selectedFont;
set => this.RaiseAndSetIfChanged(ref _selectedFont, value);
}
private string _startFolder = @"M:\Museum\ManagedCatalog";
public string StartFolder
{
get => _startFolder;
set => this.RaiseAndSetIfChanged(ref _startFolder, value);
}
// **********************************************************
// Constructor
public MainWindowViewModel()
{
SaveButtonClick = ReactiveCommand.Create(PerformSaveAction);
DummyPlaceHolder = "Current Colour Base is : 'redacted' " ;
}
private void PerformSaveAction()
{
Console.WriteLine("The \'Set / Save\' button was pressed.");
}
//*******************************************
private ColorTheme getCurrentTheme()
{
return GetRomzetronAvaloniaTheme().ColorTheme;
}
// **********************************************************
// JC added from https://github/Romzetron/Romzetron.Avalonia?tab=readme-ov-file
private static RomzetronTheme? GetRomzetronAvaloniaTheme()
{
// Get a reference to the current application.
if (Application.Current is not App app)
return null;
// Loop through the styles in the application.
foreach (var style in app.Styles)
{
// Return RomzetronTheme if it is found.
if (style is RomzetronTheme theme)
return theme;
}
return null;
}
}
And just to refocus. The issue is to get the xaml line
<TextBlock Text="xxxx" />
actually connected to lFonts
in the code behind
Thanks again
JC
Share Improve this question edited Jan 30 at 0:47 jc508 asked Jan 29 at 7:24 jc508jc508 1079 bronze badges 9 | Show 4 more comments1 Answer
Reset to default 0your Item source in the ComboBox is list of strings not list of objects so you should bind to it directly as following
<TextBlock Text="{Binding .}" />
MainWindowViewModel
we need to know what is the type ofSelectedFont
there! – Ibram Reda Commented Jan 29 at 15:46