I am very new to MVVM and I am stuck with data binding. I have a button on my view page which dynamically creates text boxes but I cannot see how I bind these textboxes to my List in ViewModel.
In my view i have:
<Button x:Name="btWebsite" Grid.ColumnSpan="2" Width="50" Height="50" Click="btWebsite_Click" Margin="23,245,259,202">
<StackPanel x:Name="pnWebsiteButton" Orientation="Horizontal">
<Image x:Name="imgWebsite" Source= "Images/webIcon.jpg" Stretch="Fill" HorizontalAlignment="Left" VerticalAlignment="Top"/>
</StackPanel>
</Button>
<GroupBox x:Name="grpWebsite" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="73,245,0,0" Grid.ColumnSpan="2" Height="51" Width="170" BorderBrush="{x:Null}" BorderThickness="0">
<ScrollViewer x:Name="pnScrollWebsite" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" Margin="0,0,0,-6">
<StackPanel x:Name="pnWebsite" Orientation="Vertical" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="1,2,0,0" VerticalAlignment="Top" IsEnabled="True">
</StackPanel>
</ScrollViewer>
</GroupBox>
The code behind the button is:
private void btWebsite_Click(object sender, RoutedEventArgs e)
{
var newTextBox = new TextBox();
newTextBox.Text = "type the website address...";
newTextBox.Foreground = Brushes.Gray;
newTextBox.Width = 150;
newTextBox.Name = "txtWebsite" + iWebsites;
pnWebsite.Children.Add(newTextBox);
pnWebsite.RegisterName(newTextBox.Name, newTextBox);
iWebsites++;
}
In my ViewModel i have:
public List<string> Websites
{
get { return _websites; }
set
{
if (value != _websites)
{
_websites = value;
OnPropertyChanged("Websites");
}
}
}
I am struggling to see how I get the website textboxes into the viewmodel list. Thank you for your help
Delete your event handler from the code-behind: btWebsite_Click.
Modify your xaml like this:
<Button x:Name="btWebsite" Grid.ColumnSpan="2" Width="50" Height="50" Command="{Binding AddNewStringCommand}" Margin="23,245,259,202">
<StackPanel x:Name="pnWebsiteButton" Orientation="Horizontal">
<Image x:Name="imgWebsite" Source= "Images/webIcon.jpg" Stretch="Fill" HorizontalAlignment="Left" VerticalAlignment="Top"/>
</StackPanel>
</Button>
<GroupBox x:Name="grpWebsite" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="73,245,0,0" Grid.ColumnSpan="2" Height="51" Width="170" BorderBrush="{x:Null}" BorderThickness="0">
<ScrollViewer x:Name="pnScrollWebsite" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" Margin="0,0,0,-6">
<StackPanel x:Name="pnWebsite" Orientation="Vertical" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="1,2,0,0" VerticalAlignment="Top" IsEnabled="True">
<ItemsControl ItemsSource="{Binding Websites}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Mode=TwoWay}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
</GroupBox>
You need to modify your ViewModel also:
public ObservableCollection<string> Websites { get; set; }
public ICommand AddNewStringCommand => new RelayCommand(AddNewString);
private void AddNewString()
{
Websites.Add(string.Empty);
}
Instead of RelayCommand you can use any implementation of ICommand. I use for example MVVMLight.
As you see, the main differences:
Instead of handling the Click event, there is a Command Binding in
the button.
Instead of generating new controls from code behind, there is an
ItemsControl that creates one every time when we have a new
element in the collection.
The new TextBox's Text property is bound to the new element.
Update:
I made a mistake, ObservableCollection is not working directly with the TextBox.
It seems you need something in addition:
public class Website
{
public string Name { get; set; }
}
Modify the ViewModel like this:
public ObservableCollection<Website> Websites { get; set; } = new ObservableCollection<Website>();
public ICommand AddNewStringCommand => new RelayCommand(AddNewString);
private void AddNewString()
{
Websites.Add(new Website {Name = "new website"});
}
And the ItemsTemplate like this:
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel >
<TextBox Text="{Binding Name, Mode=TwoWay}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
Related
The image of the list box item is too large.
I want to make this an item of constant height. Like the picture below.
This is my Code -
xaml:
<ListBox Grid.Row="0" x:Name="listBox" HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Stretch" AllowDrop="True" Drop="ListBox_Drop" DragEnter="ListBox_DragEnter" ScrollViewer.VerticalScrollBarVisibility="Visible" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<Image Margin="3" Source="{Binding Path}"/>
<TextBlock Margin="3" Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
cs:
class VideoListing
{
public string Name { get; set; }
public string Path { get; set; }
}
List<VideoListing> list = new List<VideoListing>();
public VideoPanel()
{
InitializeComponent();
list.Add(new VideoListing()
{
Name = "hello",
Path = #"C:\Users\johndoe\Desktop\Screenshot.png",
});
listBox.Items.Add(list);
}
You need to set either the Height or the MaxHeight properties on the Image control. Since you say you want a "constant height" then set the height of the image and tell it to scale the source appropriately.
<StackPanel Orientation="Vertical">
<Image Margin="3" Source="{Binding Path}" Height="64" Stretch="Uniform"/>
<TextBlock Margin="3" Text="{Binding Name}"/>
</StackPanel>
use the listView control in your application and use imagelist control to hold many images and set image index in a listview control.
I think it's too simple
I'm actually developping an UWP app who's reading Excel data, and display it on a form. Every Excel sheet are represented with RadioButton, and when the user click on a RadioButton, I update a ListView with the corresponding data.
Every item of the ListView have different data, and 2 checkboxes. This checkboxes can have the state "true" or "false", and if the user need to change the state, I want to modify his value
But the problem is, there is no event triggered with all the checkboxes when I check them. I tried to search and try to make my own template, but without success.
All the data are stored in a class :
public class REFERENCES
{
public int AI_ID { get; set; }
public int ID_poste { get; set; }
public string reference { get; set; }
public string designation { get; set; }
public bool avance { get; set; }
public bool jour { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public REFERENCES(int ai_id, int id_poste, string ref_, string des_, string avance_, string jour_)
{
AI_ID = ai_id;
ID_poste = id_poste;
reference = ref_;
designation = des_;
if(jour_ != null)
{
jour = true;
}
else
{
jour = false;
}
if (avance_ != null)
{
avance = true;
}
else
{
avance = false;
}
}
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And this is my ListView :
<ListView x:Name="ListViewData" SelectionMode="None" HorizontalAlignment="Center" Height="412" VerticalAlignment="Top" Width="650" Margin="0,218,0,0" >
<ListView.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Height="35" Margin="0,0,0,0" VerticalAlignment="Center" >
<TextBlock Text="AI_ID" Width="0" Visibility="Collapsed" />
<TextBlock Text="Désignations" FontSize="20" Width="300" Foreground="Blue" TextAlignment="Center" VerticalAlignment="Center" />
<TextBlock Text="Références" FontSize="20" Width="150" Foreground="Blue" TextAlignment="Center" VerticalAlignment="Center" />
<TextBlock Text="Avance" FontSize="20" Width="100" Foreground="Blue" TextAlignment="Center" VerticalAlignment="Center" />
<TextBlock Text="Jour" FontSize="20" Width="100" Foreground="Blue" TextAlignment="Center" VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>
</ListView.HeaderTemplate>
<ListView.ItemTemplate>
<DataTemplate x:DataType="ExcelLinkData:REFERENCES">
<StackPanel Orientation="Horizontal" >
<TextBlock Name="ItemAI_ID" Text="{x:Bind AI_ID}" Width="0" />
<TextBlock Name="ItemDesignation" Text="{x:Bind designation}" Width="300" FontSize="16" Height="55" VerticalAlignment="Center" HorizontalAlignment="Center" TextAlignment="Center"/>
<TextBlock Name="ItemReference" Text="{x:Bind reference}" Width="150" FontSize="16" Height="55" VerticalAlignment="Center" HorizontalAlignment="Center" TextAlignment="Center" />
<Grid Width="100" Height="55" VerticalAlignment="Center" HorizontalAlignment="Center">
<CheckBox Name="ItemAvance" IsChecked="{x:Bind avance}" Tag="{x:Bind AI_ID}" Checked="CHANGE_STATUS_REFERENCE"/>
</Grid>
<Grid Width="100" Height="55" VerticalAlignment="Center" HorizontalAlignment="Center">
<CheckBox Name="ItemJour" IsChecked="{x:Bind jour}" Tag="{x:Bind AI_ID}" Checked="CHANGE_STATUS_REFERENCE"/>
</Grid>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The method "CHANGE_STATUS_REFERENCE" is the method where I want to change the state in my class.
I tried different solutions, but I'm not an expert in UWP, so if anyone got an advice, I'll take it !
Thanks in advance for your time !
Guillaume
In UWP, default mode for Binding is OneTime. And when you change property from viewmodel then no event is triggered. By changing Binding to OneWay / TwoWay (depending on your usage), viewmodel will trigger the event.
Change
IsChecked="{x:Bind jour}"
To
IsChecked="{x:Bind jour Mode=TwoWay}"
Change
<CheckBox Name="ItemAvance" IsChecked="{x:Bind avance}" Tag="{x:Bind AI_ID}" Checked="CHANGE_STATUS_REFERENCE"/>
to
<CheckBox Name="ItemAvance" IsChecked="{x:Bind avance}" Tag="{x:Bind AI_ID}" Command="{Binding CHANGE_STATUS_REFERENCE}"/>
Add a "Flip Function" to your class:
private void Flip()
{
this.avance = !this.avance;
}
Set up a RelayCommand:
RelayCommand _flipCommand = new RelayCommand(this.Flip);
and Implement CHANGE_STATUS_REFERENCE as an ICommand like so:
public ICommand CHANGE_STATUS_REFERENCE
{
get
{
return _flipCommand;
}
}
For my code, I've created multiple control of the same type (i.e. Checkbox) using itemscontrol bound to a class "MyClass". I've named the checkbox control as "checkControl". Now, as I've multiple checkbox control created in the UI, I want to check their state and differentiate among them. How should I proceed? I'm thinking of using findvisualchild & findvisualparent? But, I don't have any idea how to use it?
<ItemsControl Name="myList">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="30,80,10,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<Border x:Name="tempBorder" BorderBrush="LightGray" BorderThickness="2,2,2,2" CornerRadius="4,4,4,4" Margin="0,-30,-60,-30"
Background="LightGray">
<StackPanel Orientation="Horizontal" Background="Transparent" Margin="0">
<StackPanel Background="White" Orientation="Horizontal">
<CheckBox x:Name="checkControl" Margin="7,7,0,0" Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked">
<WrapPanel>
<Image Source="/Images/myimage.png" Margin="0,10,0,0"></Image>
<StackPanel Orientation="Vertical" VerticalAlignment="Center">
<TextBlock Text="{Binding Disk}" FontFamily="roboto" FontSize="14"/>
<Rectangle Width="340" Height="1" Margin="0,5,5,5" Fill="Black"/>
<TextBlock>
<Run Text="Item:" FontFamily="roboto" FontSize="14"/>
<Run Text="{Binding Path=Name, StringFormat=' {0} Jr.'}" FontSize="20" Foreground="Orange"
FontWeight="DemiBold"/>
</TextBlock>
</StackPanel>
</WrapPanel>
</CheckBox>
</StackPanel>
<StackPanel Orientation="Vertical" Background="LightGray" Margin="0" HorizontalAlignment="Center" Width="765"
VerticalAlignment="Center">
<TextBlock Text="Select the Option:" Margin="15,7,0,7" FontFamily="roboto"/>
<ComboBox x:Name="comboControl" Margin="15,5,0,7" Width="750" SelectionChanged="comboControl_SelectionChanged"/>
</StackPanel>
</StackPanel>
</Border>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
For My Backend C# Code:
Class
public class MyClass
{
public string Disk { get; set; }
public string Name { get; set; }
public MyClass()
{
}
public MyClass(string album, string name)
{
this.Disk = album;
this.Name = name;
}
}
In my Xaml.cs
public ObservableCollection<MyClass> StudentDisk { get; set; }
//somecode
StudentDisk.Add(new MyClass("Disk 4 ", "John")); //For populating
//somecode
myList.ItemsSource = StudentDisk;
it is possible to differentiate checkBoxes by their DataContext, which should be unique:
void CheckBox_Checked(object sender, RoutedEventArgs e)
{
var checkbox = (CheckBox)sender;
var item = (MyClass)checkbox.DataContext;
MessageBox.Show(item.Disk + " " + item.Name);
}
I have a ListView with a DataTemplate, inside this templates there are several textblocks and a button. The button has a contextmenu with fixed items. The binding of the listviewitems works fine, but binding a property to the contextmenu does not seem to work.
<ListView x:Name="lv_clients" Margin="0 22 0 0" SelectionMode="Single">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid Grid.Column="0" Grid.RowSpan="2" Background="{Binding StateColor}">
</Grid>
<TextBlock Grid.Column="1" Text="{Binding DisplayString}" Foreground="Black" Height="20" FontWeight="Bold" Padding="2,2,0,0" />
<Button Click="Button_ListItem_Click" Grid.Column="1" Grid.Row="0" HorizontalAlignment="Right" VerticalAlignment="Top">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Anrufen" Name="mn_call" Click="mn_call_Click" DataContext={Binding Number} />
</ContextMenu>
</Button.ContextMenu> ...</Button>
<StackPanel Grid.Column="1" Grid.Row="1">
<TextBlock Text="{Binding State}" Height="20" Padding="2,2,0,0"/>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FirstEntry.KDKGRS}" Height="20" FontWeight="Bold" Padding="2,2,2,0" HorizontalAlignment="Left" Foreground="{Binding FirstEntry.ConvertedKGFARBE}" />
<TextBlock Text="{Binding FirstEntry.ADNAMI}" Height="20" Padding="0,2,0,0" HorizontalAlignment="Left" />
</StackPanel>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I have removed some unnecessary bits of the code (style and columndefinitions) for readability.
The important part is the MenuItem inside the Button. My underlying class has a public string property Number. This should be passed to the menu item. But the DataContext of the MenuItem is always null inside the click event.
I've read something about the contextmenu not beeing part of the visual tree, but I can't wrap my head around it. Could somebody explain the issue with the binding?
Edit Code for the underlying class:
Again removed some unnecessary code for the question
public class PhoneClient
{
public String Name { get; set; }
public String Number { get; set; }
public String Extension { get; set; }
public String DisplayString
{
get
{
return String.IsNullOrEmpty(Name) ? Number : String.Format("{0} ({1})", Name, Extension);
}
}
}
And the binding of the ListBox:
List<PhoneClient> clients = new List<PhoneClient>();
clients = load(); //returns active Clients
lv_clients.ItemsSource = clients;
Bridging the gap
<ContextMenu Tag="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Anrufen" Click="mn_call_Click" Name="mn_call"
DataContext="{Binding Tag.Number, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}" />
</ContextMenu>
Handler
private void mn_call_Click(object sender, RoutedEventArgs e)
{
MenuItem currentMenuItem = (MenuItem)sender;
string number = (string)currentMenuItem.DataContext;
// Do Stuff
}
EDIT
MainWindow.xaml
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid Margin="10">
<ListView x:Name="lv_clients" Margin="0 22 0 0" SelectionMode="Single">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Background="LightGray" Width="100">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid Background="LightGreen">
</Grid>
<TextBlock Text="{Binding DisplayString}" Foreground="Black" Height="20" FontWeight="Bold" Padding="2,2,0,0" />
<Button Click="Button_ListItem_Click" Grid.Row="0" HorizontalAlignment="Right" VerticalAlignment="Top">
<Button.ContextMenu>
<ContextMenu Tag="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Show" Click="mn_call_Click" Name="mn_call" DataContext="{Binding Tag.Number, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}" />
</ContextMenu>
</Button.ContextMenu> ...
</Button>
<StackPanel Grid.Row="1">
<TextBlock Text="{Binding State}" Height="20" Padding="2,2,0,0"/>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Foo" Height="20" FontWeight="Bold" Padding="2,2,2,0" HorizontalAlignment="Left" Foreground="Blue" />
<TextBlock Text="Bar" Height="20" Padding="0,2,0,0" HorizontalAlignment="Left" />
</StackPanel>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Collections.Generic;
namespace WpfApp
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<PhoneClient> clients = new List<PhoneClient>();
clients.Add(new PhoneClient() { Name = "Kumar", Number = "0101010", Extension = "555", State = "New York" });
clients.Add(new PhoneClient() { Name = "Shanaya", Number = "1010101", Extension = "555", State = "New Jersey" });
clients.Add(new PhoneClient() { Name = "Billy Bob", Number = "6543210", Extension = "555", State = "Single" });
lv_clients.ItemsSource = clients;
}
public class PhoneClient
{
public String Name { get; set; }
public String Number { get; set; }
public String Extension { get; set; }
public String State { get; set; }
public String DisplayString
{
get
{
return String.IsNullOrEmpty(Name) ? Number : String.Format("{0} ({1})", Name, Extension);
}
}
}
private void mn_call_Click(object sender, RoutedEventArgs e)
{
MenuItem currentMenuItem = (MenuItem)sender;
string number = (string)currentMenuItem.DataContext;
MessageBox.Show("Number " + number);
}
private void Button_ListItem_Click(object sender, RoutedEventArgs e)
{
Button currentButton = (Button)sender;
PhoneClient data = (PhoneClient)currentButton.DataContext;
MessageBox.Show(data.Name + " tapped");
}
}
}
I have a Pivot view in my WP8 app, and I'm trying to access the data collection from the view's class upon Click event "removePin".
The source of the data collection is another class called Pins.
how can I achieve that?
this is my snippet code for the XAML for that particular part
<phone:PivotItem Header="Pins">
<!-- Content Panel -->
<Grid x:Name="ContentPanel2" HorizontalAlignment="Left" Height="583" Margin="10,10,0,0" Grid.Row="1" VerticalAlignment="Top" Width="460">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="400*"/>
<ColumnDefinition Width="0*"/>
<ColumnDefinition Width="87*"/>
</Grid.ColumnDefinitions>
<ListBox x:Name="lstData2" ItemsSource="{Binding DataCollection2, Source={StaticResource PinsCollection}}"
Grid.ColumnSpan="3" Foreground="#FF1D53D0" Height="583" VerticalAlignment="Bottom">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Tap="StackPanel_Tap">
<Image Margin="8" VerticalAlignment="Top" Source="{Binding ImageUri}"
Width="100" Height="100" />
<StackPanel Height="93" Width="259" >
<TextBlock Margin="8" Width="250" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Left" Foreground="#FF1D53D0"
Text="{Binding Pinnedname}" Height="33" RenderTransformOrigin="0.5,0.5" FontFamily="Segoe WP SemiLight" FontSize="24" FontWeight="Bold" />
<TextBlock Width="155" Margin="8,0,8,8" VerticalAlignment="Top"
HorizontalAlignment="Left" Text="{Binding Status}" Foreground="#FF1D53D0" FontFamily="Segoe WP SemiLight" />
</StackPanel>
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu>
<toolkit:MenuItem Header="Remove Pin" Click="RemovePin_Click" Tag="{Binding pinId}"/>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<!-- End of Content Panel -->
</Grid>
Create a custom command class:
public class CustomCommand : ICommand
{
Action _exec;
public CustomCommand(Action exec)
{
_exec = exec;
}
public void Execute(object parameter)
{
if (_exec != null) _exec();
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
}
Add it to the view model for elements of pins collection (I supposed it's PinItem)
public CustomCommand RemovePinCommand
{
get { return (CustomCommand)GetValue(RemovePinCommandProperty); }
set { SetValue(RemovePinCommandProperty, value); }
}
public static readonly DependencyProperty RemovePinCommandProperty =
DependencyProperty.Register("RemovePinCommand",
typeof(CustomCommand),
typeof(PinItem),
new UIPropertyMetadata(null));
In the constructor of that class implement your logic for this command:
RemovePinCommand = new CustomCommand(() =>
{
//delete this pin from its parent collection
});
final step is to bind the command to the menu item:
<toolkit:MenuItem Header="Remove Pin" Command="{Binding RemovePinCommand}"/>