I'm new to WPF/XAML and have some decent problems with data-binding und layout.
What i try to achieve:
a List with a single Row of Grid Cells (just Text and BackgroundColor) and below some Textblocks. My XAML looks like this:
<Window.Resources>
<DataTemplate x:Key="GridLayoutTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Rectangle Grid.Row="0" Grid.Column="0" Fill="{Binding RowData.RowColor}" />
<TextBlock Text="{Binding RowData.RowText}" />
</Grid>
</DataTemplate>
<DataTemplate x:Key="ListLayoutTemplate">
<StackPanel VerticalAlignment="Stretch">
<ItemsControl ItemTemplate="{StaticResource GridLayoutTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="1" Columns="9"></UniformGrid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<Grid Background="Green">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Rectangle Grid.Row="0" Grid.Column="0" Fill="{Binding TileColor}" />
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding TileText}" />
</Grid>
</StackPanel>
</DataTemplate>
</Window.Resources>
<ListView ItemTemplate="{StaticResource ListLayoutTemplate}" Name="lvDataBinding">
</ListView>
Code behind:
public Test()
{
InitializeComponent();
List<DataObject> items = new List<DataObject>();
List<Row> rowItem = new List<Row>();
rowItem.Add(new Row()
{
RowColor = "Red",
RowText = "Text1"
});
rowItem.Add(new Row()
{
RowColor = "Blue",
RowText = "Text2"
});
items.Add(new DataObject()
{
TileColor = "Black",
TileText = "Blibb",
RowData = rowItem
});
items.Add(new DataObject() { TileColor = "Yellow", TileText = "Blubb", RowData=rowItem });
items.Add(new DataObject() { TileColor = "Red", TileText = "Blabb", RowData=rowItem });
this.lvDataBinding.ItemsSource = items;
}
}
public class DataObject
{
public string TileText { get; set; }
public string TileColor { get; set; }
public List<Row> RowData { get; set; }
}
public class Row
{
public string RowText { get; set; }
public string RowColor { get; set; }
}
If i run this it only shows the rows for the listview and not the uniformgrid.
the binding for the listview item is ok, but i have no idea for the uniformgrid, the layout could also be wrong. confusing over all.
Bind the ItemsSource property of the ItemsControl to RowData and remove "RowData." from the binding paths in the GridLayoutTemplate:
<DataTemplate x:Key="GridLayoutTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Rectangle Grid.Row="0" Grid.Column="0" Fill="{Binding RowColor}" />
<TextBlock Text="{Binding RowText}" />
</Grid>
</DataTemplate>
<DataTemplate x:Key="ListLayoutTemplate">
<StackPanel VerticalAlignment="Stretch">
<ItemsControl ItemsSource="{Binding RowData}" ItemTemplate="{StaticResource GridLayoutTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="1" Columns="9"></UniformGrid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<Grid Background="Green">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Rectangle Grid.Row="0" Grid.Column="0" Fill="{Binding TileColor}" />
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding TileText}" />
</Grid>
</StackPanel>
</DataTemplate>
Related
Thanks in advance for any support! I am trying to create a custom tab item to act as a canvas for dynamically creating UI elements. Here is an image to give an idea as to what I would like in this custom control:
I need to be able to generate the Tab Items dynamically to the TabControl in a parent form - the problem is that my code seems to do nothing to the TabItem - it's just always blank and it doesn't complain about my code. What's missing? Thanks for any help!
My WPF user control tabitem Code:
<UserControl x:Class="Configuration_Manager.SearchTab"
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:Configuration_Manager"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<TabItem Header="Search Configuration Name">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<GroupBox Header="Git Repo Credentials:">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="Server Address:" />
<TextBox Grid.Column="1" Margin="2" />
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="Username:" />
<TextBox Grid.Column="1" Margin="2" />
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="Password:" />
<TextBox Grid.Column="1" Margin="2" />
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<CheckBox x:Name="CheckBoxStoreCredentials" Content="Store Credentials" Grid.Column="0" IsChecked="False" VerticalAlignment="Center"/>
<Button x:Name="ButtonDownloadConfiguration" Content="Download Configuration" Grid.Column="2" Margin="5" />
</Grid>
</StackPanel>
</GroupBox>
</Grid>
</StackPanel>
</TabItem>
</UserControl>
The Designer:
In WPF if you want to dynamically create controls you always have to use templates. TabControl is an ItemsControl. TabItem elements (the tabs) are automatically generated for each item inside the ItemsControl.ItemsSource collection. The visual of this TabItem can be designed by using styles and templates.
Use the TabControl.ContentTemplate property to specify the DataTemplate for the content of each tab
First you have to create the data model that should be displayed inside a TabItem.
TabData.cs:
public class TabData : INotifyPropertyChanged
{
public TabData(string header)
{
this.Header = header;
}
public string Header { get; set; }
private string serverAddress;
public string ServerAddress
{
get => this.serverAddress;
set
{
if (value == this.serverAddress) return;
this.serverAddress = value;
OnPropertyChanged();
}
}
private string username;
public string Username
{
get => this.username;
set
{
if (value == this.username) return;
this.username = value;
OnPropertyChanged();
}
}
private string password;
public string Password
{
get => this.password;
set
{
if (value == this.password) return;
this.password = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Then create a view model that handles the tab data and exposes the items source for the TabControl.
The view model is the DataContext of the TabControl.
ViewModel.cs
public class ViewModel: INotifyPropertyChanged
{
public ViewModel()
{
this.TabDatas = new ObservableCollection<TabData>()
{
new TabData("First Tab"),
new TabData("Second Tab"),
new TabData("Third Tab")
};
}
// Adding a new TabData item to the TabDatas collection
// will dynamically create a new TabItem inside the TabControl
public void AddNewTab()
{
this.TabDatas.Add(new TabData("Fourth Tab"));
}
public ObservableCollection<TabData> TabDatas { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
AddNewTab can be invoked from a ICommand (e.g. on button clicked) or some event (e.g. new data available).
MainWindow.xaml:
<Window>
<Window.DataContext>
<ViewModel />
</Window.DataContext>
<Grid>
<!-- Use DisplayMemberPath property to set the source property for the tab header -->
<TabControl x:Name="TabControl"
ItemsSource="{Binding TabDatas}"
DisplayMemberPath="Header" >
<!-- Use a DataTemplate to design the visual appearance of the TabItem content -->
<TabControl.ContentTemplate>
<DataTemplate DataType="TabData">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<GroupBox Header="Git Repo Credentials:">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0"
Content="Server Address:" />
<TextBox Grid.Column="1"
Margin="2"
Text="{Binding ServerAddress}" />
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0"
Content="Username:" />
<TextBox Grid.Column="1"
Margin="2"
Text="{Binding Username}"/>
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0"
Content="Password:" />
<TextBox Grid.Column="1"
Margin="2"
Text="{Binding Password}"/>
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<CheckBox x:Name="CheckBoxStoreCredentials"
Content="Store Credentials"
Grid.Column="0"
IsChecked="False"
VerticalAlignment="Center" />
<Button x:Name="ButtonDownloadConfiguration"
Content="Download Configuration"
Grid.Column="2"
Margin="5" />
</Grid>
</StackPanel>
</GroupBox>
</Grid>
</StackPanel>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</Window>
Application is WPF .netframework
I need to bind properties as itemsource to listbox from class which is in .dll. There is the main problem because if I Bind some other Collection which doesn't have Model in dll. it works, but I need to have it in library.
xmlns:c="clr-namespace:MessageLibrary;assembly=MessageLibrary"
I don't know how to fix it, I tried almost everything
I looked at MSDN and there was the exactly same example a nothing work. I'll be very thankful to somebody solve this please
Here is the ItemSourceCollection in MainViewModel
public ObservableCollection<ViewModelUser> AllUsers { get; set; }
public ObservableCollection<ViewModelRoom> AllRooms { get; set; }
AllUsers = new ObservableCollection<ViewModelUser>();
AllRooms = new ObservableCollection<ViewModelUser>();
Here is my xaml class:
<Window x:Class="ChatsUPClient.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ChatsUPClient"
xmlns:vm="clr-namespace:ChatsUPClient.ViewModel"
xmlns:c="clr-namespace:MessageLibrary;assembly=MessageLibrary"
Title="ChatsUP" Height="600" Width="900">
<Window.DataContext>
<vm:MainViewModel />
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="24"/>
<RowDefinition Height="24"/>
<RowDefinition Height="*"/>
<RowDefinition Height="24"/>
</Grid.RowDefinitions>
<TextBlock x:Name="UserNameTextBlock" Grid.Column="1" Grid.Row="0" Text="{Binding Path=LRName, UpdateSourceTrigger=PropertyChanged}" TextAlignment="Right"></TextBlock>
<TextBox x:Name="MessageTextBox" Grid.Column="1" Grid.Row="3" TextWrapping="NoWrap" Text="{Binding Path=Input, Mode=TwoWay}"/>
<TextBox x:Name="MessageTimeLine" Grid.Column="1" Grid.Row="1" Grid.RowSpan="2" TextWrapping="Wrap" Text="{Binding Path=Output}" Grid.ColumnSpan="2" IsReadOnly="True" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" FontFamily="Lucida Sans Typewriter"/>
<Button x:Name="SendBtn" Content="Odeslat" Grid.Column="3" Grid.Row="3" Command="{Binding Path=ProcessCommand}"/>
<Button x:Name="CreateRoomBtn" Content="Vytvořit místnost"/>
<TextBox x:Name="CreateRoomName" Grid.Row="1"></TextBox>
<Grid Grid.Row="2" RenderTransformOrigin="0.505,0.438">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ListBox Grid.Column="0" Grid.Row="2" ItemsSource="{Binding AllRooms}" >
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type c:ViewModelUser}">
<TextBlock Text="{Binding Path= NameOfconversation}"></TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox Grid.Column="0" Grid.Row="0" ItemsSource="{Binding AllUsers}">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type c:ViewModelUser}">
<TextBlock Text="{Binding NickName}"> </TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
<Button x:Name="Entry" Grid.Row="3" Grid.Column="0" Content="Vstoupit"></Button>
<StackPanel Grid.Column="1" Grid.Row="2" VerticalAlignment="Center" x:Name="LoginPanel">
<TextBlock HorizontalAlignment="Center">Enter Name and Password</TextBlock>
<TextBox x:Name="NameTextBox" Text="{Binding Path=LRName}"/>
<TextBox x:Name="PasswordTextBox" Text="{Binding Path=LRPassword}"/>
<Button Content="Přihlásit se/Registrovat" Command="{Binding Path=LogingCommand}" Click="OnSave" />
</StackPanel>
</Grid>
class in library looks like this:
public class ViewModelUser
{
public int UserId { get; set; }
public string NickName { get; set; }
public string Password { get; set; }
}
I'm building a School App that can display your grades.
I have the following DataStructure:
public class Rootobject
{
public List<Subject> subjects{ get; set; }
}
public class Subject
{
public String name { get; set; }
public int id { get; set; }
public String teacher { get; set; }
public GradeSet wirtten { get; set; }
public GradeSet spoken { get; set; }
public float average { get; set; }
}
public class GradeSet
{
public float counts { get; set; }
public List<Grade> grades { get; set; }
public float average { get; set; }
}
public class Grade
{
public string name { get; set; }
public string grade { get; set; }
}
I have an ObservableCollection from the type of "Subject"
subjects = new ObservableCollection<Subject>();
I have 3 ListViews. One shows all the Subjects (name & teacher). That works already.
How I bound it:
<ListView Name="SubjectsListView" IsItemClickEnabled="True" ItemsSource="{x:Bind subjects}" ItemClick="FacherListView_ItemClick">
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:Subject">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{x:Bind name}" FontSize="20" Margin="4,0,0,0" />
<TextBlock Text="{x:Bind teacher}" Grid.Row="1" Margin="4,4,0,0" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
In the other 2 ListViews, in the first, I want to display the written grades (name & the grade itself), in the second, I want to display the spoken grades (name & the grade itself).
The written and spoken grades ListView look the same, but how do I bind the grades and names to them?
This is the ListView:
<ListView Name="gradeView" Grid.Column="0" HorizontalContentAlignment="Stretch" Grid.Row="2" SelectionMode="None">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical">
<TextBlock Name="GradeName" Text="The name of the grade" FontSize="20" FontWeight="Bold" />
<TextBlock Name="GradeName" Text="the grade (B+)" FontSize="20" />
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Since there is a written and spoken grade per subject you could bind the ItemsSource property of the "gradeView" to the SelectedItem property of the "SubjectsListView":
<ListView Name="gradeView" Grid.Column="0" HorizontalContentAlignment="Stretch" Grid.Row="2" SelectionMode="None"
ItemsSource="{Binding SelectedItem.wirtten.grades, ElementName=SubjectsListView}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:Grade">
<Grid Margin="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical">
<TextBlock Name="GradeName" Text="{x:Bind name}" FontSize="20" FontWeight="Bold" />
<TextBlock Name="GradeName" Text="{x:Bind grade}" FontSize="20" />
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
It is almost the same for the third ListView. Just change the path of the ItemsSource binding:
<ListView Name="gradeView2" Grid.Column="0" HorizontalContentAlignment="Stretch" Grid.Row="2" SelectionMode="None"
ItemsSource="{Binding SelectedItem.spoken.grades, ElementName=SubjectsListView}">
The second and third ListViews should then be populated as you select a corresponding subject in the first ListView.
This is untested, but you might try adding a nested ListView to display the grades, like this:
<ListView Name="SubjectsListView"
IsItemClickEnabled="True"
ItemsSource="{x:Bind subjects}"
ItemClick="FacherListView_ItemClick">
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:Subject">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{x:Bind name}"
FontSize="20"
Margin="4,0,0,0" />
<ListView IsItemClickEnabled="True"
ItemsSource="{x:Bind wirtten.grades}"
Grid.Row="1"
Margin="4,4,0,0">
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:Grade">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{x:Bind name}"
FontSize="20"
Margin="4,0,0,0" />
<TextBlock Text="{x:Bind grade}"
Grid.Row="1"
Margin="4,4,0,0" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Is there any way to get the index of the data which is binded to the ItemsControl, when a button is clicked?
<ItemsControl x:Name="ic_schranke" DataContext="{Binding Schranke}" >
<ItemsControl.ItemTemplate>
<DataTemplate >
<Button Height="80" x:Name="btn_open" Click="btn_open_Click" HorizontalAlignment="Stretch" Style="{StaticResource TransparentStyle}">
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="30*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Rectangle x:Name="rect" Grid.ColumnSpan="3" Grid.Row="1" Opacity="0.65" Grid.Column="0" Fill="#FFCEEAFF"/>
<Border CornerRadius="25" Height="50" Width="50" HorizontalAlignment="Center" Grid.Column="0" Grid.Row="1">
<Border.Background>
<ImageBrush ImageSource="/Assets/schranken.jpg" />
</Border.Background>
</Border>
<TextBlock Name="schranken_name" Grid.Column="1" Grid.Row="1" Text="{Binding name}" VerticalAlignment="Center" HorizontalAlignment="Center" FontWeight="ExtraLight" Foreground="Black" />
</Grid>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Here is the sample data which is binded to the IC:
List<Schranke> elements;
public UserPage()
{
this.InitializeComponent();
elements = new List<Schranke>();
for (var i = 1; i < 15; i++)
elements.Add(new Schranke() { name = $"Schranke NR:{i}" });
this.ic_schranke.ItemsSource = elements;
}
And here is the code in the button click event:
private async void btn_open_Click(object sender, RoutedEventArgs e)
{
Grid grid = (sender as Button).Content as Grid;
//Debug.WriteLine($"content {tb.Text} clicked");
var tmp_background = grid.Background;
grid.Background = new SolidColorBrush(new Windows.UI.Color() { A = 255, R = 78, G = 170, B = 44 });
VibrationDevice testVibrationDevice = VibrationDevice.GetDefault();
testVibrationDevice.Vibrate(System.TimeSpan.FromMilliseconds(150));
await Task.Delay(3000);
grid.Background = tmp_background;
}
Maybe something along the lines of this:
the presenter - put this in the data context
public class SchrankePresenter : INotifyPropertyChanged
{
private List<Schranke> _elements;
public List<Schranke> Elements
{
get { return _elements; }
set
{
_elements = value;
OnPropertyChanged("Elements");
}
}
public ICommand ClickCommand { get; set; }
private void OnPropertyChanged(string propName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
public event PropertyChangedEventHandler PropertyChanged;
public SchrankePresenter()
{
var elements = new List<Schranke>();
for (var i = 1; i < 15; i++)
elements.Add(new Schranke() { Name = $"Schranke NR:{i}" });
Elements = elements;
ClickCommand = new DelegateCommand(ClickAction);
}
public void ClickAction(Schranke item)
{
VibrationDevice.GetDefault().Vibrate(TimeSpan.FromMilliseconds(150));
}
}
public class Schranke
{
public string Name { get; set; }
}
the template:
<ListView ItemsSource="{Binding Elements}">
<ListView.ItemTemplate>
<DataTemplate>
<Button Height="80"
HorizontalAlignment="Stretch"
Command="{Binding ClickCommand}"
Style="{StaticResource TransparentStyle}">
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="30*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Rectangle x:Name="rect"
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="3"
Fill="#FFCEEAFF"
Opacity="0.65" />
<Border Grid.Row="1"
Grid.Column="0"
Width="50"
Height="50"
HorizontalAlignment="Center"
CornerRadius="25">
<Border.Background>
<ImageBrush ImageSource="/Assets/schranken.jpg" />
</Border.Background>
</Border>
<TextBlock Name="schranken_name"
Grid.Row="1"
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontWeight="ExtraLight"
Foreground="Black"
Text="{Binding Name}" />
</Grid>
</Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
you can do the background animation using a storyboard/visualstate
EDIT : regarding the DelegateCommand, it's a simple implementation I use for my WPF apps -
internal class DelegateCommand<T> : ICommand
{
private Action<T> _clickAction;
public DelegateCommand(Action<T> clickAction)
{
_clickAction = clickAction;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_clickAction((T)parameter);
}
}
I'm learning WPF and I'm trying to do something simple. I have two classes: Candy and MyColor. The code of these two classes look like this
public class Candy
{
public MyColor Color { get; set; }
public string Name { get; set; }
}
public class MyColor
{
public string Name { get; set; }
public uint Id { get; set; }
}
(I have attached an image below to make it clearer)
I have an area in the window in which I can create a MyColor by using a textbox that inserts the MyColor.Name, and a simple logic which increments the MyColor.Id. On the other side of the window, I have a button that creates new item in a ItemsControl which holds Candy. Within this ItemsControl there is an ComboBox which I can specify Candy.Color and a TextBox which I specify the Candy.Name. Finally, when I hit the button Generate List the code should output in the TextBox below a list in the format of
Candy.Color Candy.Name
I'm trying to figure out how to automatically populate the ComboBox filled with a list of Colors I have created so I can specify the Candy color, but I don't know how to bind my data source. Also, how would I generate the text?
Currently my code looks like this
namespace QuestionToAsk
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
ObservableCollection<MyColor> Colors;
ObservableCollection<Candy> Candies;
public MainWindow()
{
InitializeComponent();
Colors = new ObservableCollection<MyColor>();
Candies = new ObservableCollection<Candy>();
Colors.Add(new MyColor() { Name = "(Unspecified)", Id = 0 });
icColors.ItemsSource = Colors;
icCandies.ItemsSource = Candies;
}
private void btnColor(object sender, RoutedEventArgs e)
{
if (txtColor.Text != "")
{
uint last_id = Colors.Last<MyColor>().Id;
Colors.Add(new MyColor() { Name = txtColor.Text, Id = last_id+1 });
txtColor.Text = "";
}
}
private void btnNewCandy(object sender, RoutedEventArgs e)
{
Candies.Add(new Candy());
}
private void btnGetList(object sender, RoutedEventArgs e)
{
//How to create the list of <Color, Name>?
}
}
public class Candy
{
public MyColor Color { get; set; }
public string Name { get; set; }
}
public class MyColor
{
public string Name { get; set; }
public uint Id { get; set; }
}
}
And my XML file looks like this:
<Window x:Class="QuestionToAsk.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:QuestionToAsk"
Title="Color Candy Maker" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<DockPanel Margin="3">
<Button Content="Add Color" Click="btnColor" DockPanel.Dock="Bottom"/>
<TextBox x:Name="txtColor" DockPanel.Dock="Bottom"/>
<ItemsControl x:Name="icColors" Grid.Column="0" Grid.Row="0" DockPanel.Dock="Top">
<ItemsControl.ItemTemplate>
<DataTemplate x:Name="tColorsTemplate">
<TextBlock Text="{Binding Name}" Name="Color" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DockPanel>
<DockPanel Grid.Column="1" Grid.Row="0" Margin="3">
<Grid DockPanel.Dock="Bottom">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Content="New Candy" Click="btnNewCandy" Grid.Column="0"/>
<Button Content="Generate List" Click="btnGetList" Grid.Column="1"/>
</Grid>
<ItemsControl Name="icCandies" DockPanel.Dock="Top">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ComboBox Name="cmbColors" Grid.Column="0">
<!-- How to bind this cmbColors to icColors? -->
</ComboBox>
<TextBox Text="{Binding Name}" Grid.Column="1" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DockPanel>
<DockPanel Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2">
<TextBox x:Name="txtColorCandy"/>
</DockPanel>
</Grid>
</Window>
EDIT
I have removed all my ideas and implemented it using your hard design :D.
The xaml code
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<DockPanel Margin="3">
<Button Content="Add Color" Click="btnColor" DockPanel.Dock="Bottom"/>
<TextBox x:Name="txtColor" DockPanel.Dock="Bottom"/>
<ItemsControl x:Name="icColors" Grid.Column="0" Grid.Row="0" DockPanel.Dock="Top">
<ItemsControl.ItemTemplate>
<DataTemplate x:Name="tColorsTemplate">
<TextBlock Text="{Binding Name}" Name="Color" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DockPanel>
<DockPanel Grid.Column="1" Grid.Row="0" Margin="3">
<Grid DockPanel.Dock="Bottom">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Content="New Candy" Click="btnNewCandy" Grid.Column="0"/>
<Button Content="Generate List" Click="btnGetList" Grid.Column="1"/>
</Grid>
<ItemsControl Name="icCandies" DockPanel.Dock="Top">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ComboBox Name="cmbColors" Grid.Column="0"
ItemsSource="{Binding Colors, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
DisplayMemberPath="Name"
SelectedItem="{Binding Color}">
</ComboBox>
<TextBox Text="{Binding Name}" Grid.Column="1" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DockPanel>
<DockPanel Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2">
<TextBox x:Name="txtColorCandy" VerticalScrollBarVisibility="Auto"/>
</DockPanel>
</Grid>
Note the usage of RelativeSource since the values of ComboBox was in a property of the Window class while the selected value was to be saved in the Candy class. So the ItemsSource is bound to a property of Window class and the SelectedItem is bound to a property of the Candy class
Code Behind
public partial class MainWindow : Window
{
private ObservableCollection<MyColor> _colors;
public IEnumerable<MyColor> Colors
{
get { return _colors; }
}
private ObservableCollection<Candy> _candies;
public IEnumerable<Candy> Candies
{
get { return _candies; }
}
public MainWindow()
{
InitializeComponent();
_colors = new ObservableCollection<MyColor>();
_candies = new ObservableCollection<Candy>();
_colors.Add(new MyColor { Name = "(Unspecified)", Id = 0 });
icColors.ItemsSource = Colors;
icCandies.ItemsSource = Candies;
}
private void btnColor(object sender, RoutedEventArgs e)
{
if (txtColor.Text != "")
{
uint last_id = Colors.Last<MyColor>().Id;
_colors.Add(new MyColor() { Name = txtColor.Text, Id = last_id + 1 });
txtColor.Text = "";
}
}
private void btnNewCandy(object sender, RoutedEventArgs e)
{
_candies.Add(new Candy());
}
private void btnGetList(object sender, RoutedEventArgs e)
{
StringBuilder sb = new StringBuilder();
foreach (var item in _candies)
{
if (item.Name == null || item.Color == null)
continue;
sb.AppendLine(item.Color.Name + " " + item.Name);
}
txtColorCandy.Text = sb.ToString();
}
}
It there are any confusion let me know and I will try to help