How to bind events and properties of controls in UniformGrid dynamically? - c#

I am a newbie in templating Wpf controls. I use VS2013, WPF 4.5 and Caliburn Micro 2.0.2. In part of tasks I have I need to populate a grid with toggle buttons contained different images and its subtitle. I have solved it using UniformGrid. See my code below. They work but still don't have event and property binding since I don't know how I can bind the events and properties of toggle buttons to view model, since they are generated automatically and dynamically and the number of toggle buttons is uncertain (depends on the number of images in the image folder).
For example:
manually I could bind the Click event, IsChecked property and some other properties of toggle button 1 like following:
<ToggleButton x:Name="ToggleVehicle01" IsChecked={Binding SelectedVehicle01} Background="{Binding BackColorSelectedVehicle01}" ToolTip="{Binding VehicleName01}">
But now I can't do that anymore since the toggle buttons are generated automatically and their number is uncertain. Please help. Feel free to change my code below or give me examples code that works. Thank you in advance.
The View (MainView.xaml):
<UserControl x:Class="CMWpf02.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<Grid Width="1024"
Height="768"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ShowGridLines="True">
<ItemsControl Name="ImageList"
Background="#FFFFFFFF"
BorderBrush="#FFA90606"
ItemsSource="{Binding Path=VehicleImages}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Margin="0,0,0,0" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ToggleButton Width="180"
Margin="10,10,10,10"
FontSize="10"
Style="{StaticResource {x:Static ToolBar.ToggleButtonStyleKey}}">
<!-- x:Name="ToggleVehicle01" -->
<!-- Background="{Binding BackColorSelectedVehicle01}" -->
<!-- IsChecked="{Binding SelectedVehicle01}" -->
<!-- ToolTip="{Binding Vehicle01Name}"> -->
<StackPanel Margin="0,5,0,5"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Image Width="140"
RenderOptions.BitmapScalingMode="Fant"
Source="{Binding Path=Image}" />
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
FontWeight="Bold"
Text="{Binding Path=Name}" />
</StackPanel>
</ToggleButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
The ViewModel (MainViewModel.cs):
using Caliburn.Micro;
using System;
using System.Collections.ObjectModel;
using System.IO;
namespace CMWpf02.ViewModels
{
public class MainViewModel : Screen, IHaveDisplayName
{
private String _path2Images = #"D:\tmp\Images";
public string DisplayName { get; set; }
public ObservableCollection<VehicleImage> VehicleImages { get; set; }
public MainViewModel()
{
DisplayName = "Main Window";
var vehicles = new ObservableCollection<String>();
vehicles = GetAllFilesFromFolder(_path2Images);
VehicleImages = new ObservableCollection<VehicleImage>();
foreach (var i in vehicles)
VehicleImages.Add(new VehicleImage(i));
}
public ObservableCollection<String> GetAllFilesFromFolder(String fullPathFolder)
{
string[] fileArray = Directory.GetFiles(fullPathFolder);
return new ObservableCollection<String>(fileArray);
}
}
public class VehicleImage
{
public String Image { get; private set; }
public String Name { get; private set; }
public VehicleImage(String image)
{
Image = image;
Name = Path.GetFileName(image);
}
}
//public void ToggleVehicle01()
//{
// var selectText = (SelectedVehicle01) ? " selected" : " unselected";
// MessageBox.Show(Vehicle01Name + selectText);
// BackColorSelectedVehicle01 = (SelectedVehicle01) ? _backColorSelectedVehicle : _defaultBackColorVehicle;
//}
//public Boolean SelectedVehicle02
//{
// get { return _selectedVehicle02; }
// set
// {
// _selectedVehicle02 = value;
// NotifyOfPropertyChange(() => SelectedVehicle02);
// }
//}
//public Brush BackColorSelectedVehicle02
//{
// get { return _backColorSelectedVehicle02; }
// set
// {
// _backColorSelectedVehicle02 = value;
// NotifyOfPropertyChange(() => BackColorSelectedVehicle02);
// }
//public String Vehicle01Name { get; private set; }
}
EDIT: Now I can bind the properties of generated ToggleButton with view model. I make the VehicleImage class to a view model (see modified code below). But I still have problem to bind Click-event of generated ToggleButton to view model.
The modified class to view model
public class VehicleImage : PropertyChangedBase
{
public String Image { get; private set; }
public String Name { get; private set; }
private Boolean _selectedVehicle;
public Boolean SelectedVehicle
{
get { return _selectedVehicle; }
set
{
_selectedVehicle = value;
BackColorSelectedVehicle = _selectedVehicle ? new SolidColorBrush(Color.FromArgb(255, 242, 103, 33)) : new SolidColorBrush(Colors.White);
}
}
private Brush _backColorSelectedVehicle;
public Brush BackColorSelectedVehicle
{
get { return _backColorSelectedVehicle; }
set
{
_backColorSelectedVehicle = value;
NotifyOfPropertyChange(() => BackColorSelectedVehicle);
}
}
// ToggleButton's Click-Event Handler, but it doesn't get event trigger from View.
// Therefore I set the BackColorSelectedVehicle fin setter of SelectedVehicle property.
public void ToggleSelection()
{
//BackColorSelectedVehicle = SelectedVehicle ? new SolidColorBrush(Color.FromArgb(255, 242, 103, 33)) : new SolidColorBrush(Colors.White);
}
public VehicleImage(String image)
{
Image = image;
Name = Path.GetFileName(image);
}
}
The modified view
<ToggleButton Width="180"
Margin="10,10,10,10"
Background="{Binding Path=BackColorSelectedVehicle}"
FontSize="10"
IsChecked="{Binding Path=SelectedVehicle}"
Style="{StaticResource {x:Static ToolBar.ToggleButtonStyleKey}}"
ToolTip="{Binding Path=Name}">
<!-- x:Name="ToggleSelection" -->
<StackPanel Margin="0,5,0,5"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Image Width="140"
RenderOptions.BitmapScalingMode="Fant"
Source="{Binding Path=Image}" />
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding Path=Name}" />
</StackPanel>
</ToggleButton>

Related

Devexpress wpf CellEditor bind to class instance

I have dxg:GridControl.
xaml:
<dxg:GridControl Name="DynamicGridControl"
ItemsSource="{Binding CommonEditCollection, Mode=TwoWay}"
SelectionMode="Cell"
AutoGenerateColumns="AddNew"
AutoGeneratedColumns="GridControl_AutoGeneratedColumns">
<dxmvvm:Interaction.Behaviors>
<lc:CellSelectionBehavior SelectedCells="{Binding SelectedCells, Mode=TwoWay}"/>
</dxmvvm:Interaction.Behaviors>
</dxg:GridControl>
ItemsSource binds to CommonEditCollection
viewmodel:
public ObservableCollection<Dictionary<int, DynamicTableModel>> CommonEditCollection { get; set; }
model:
public class DynamicTableModel
{
public double CellWidth { get; set; }
public string StrValue{ get; set; }
public bool IsBorerNull { get; set; }
public DynamicTableModel(string strVal, double cellWidth, bool isBorerNull = false)
{
StrValue = strVal;
CellWidth = cellWidth;
IsBorerNull = isBorerNull;
}
}
In xaml file I set Resources for cells style (I want to merge some cells):
<DataTemplate x:Key="CellDataTemplate">
<StackPanel>
<Border ...
</Border.Style>
</Border>
<dxg:CellEditor Content="{Binding Value.StrValue}"/>
</StackPanel>
</DataTemplate>
I bind CellEditor to property of DynamicTableModel class. But if I try edit text in any cell it throw NullReferenceException.
I cant change class DynamicTableModel to string because I need other properties. And I tried to use convertor Attribute, but it create new instance when I change text.
Help me please to change text in cells.
Project link: https://github.com/Kolgotin/DynamicGridControl
In result I just added this property:
<DataTemplate>
<dxe:TextEdit Name="PART_Editor" HorizontalContentAlignment="Stretch">
<dxe:TextEdit.EditTemplate>
<ControlTemplate>
<dxe:TextEdit x:Name="teNewValue"
HorizontalAlignment="Stretch"
EditValue="{Binding Value.StrValue}"/>
</ControlTemplate>
</dxe:TextEdit.EditTemplate>
</dxe:TextEdit>
</DataTemplate>
and handler works in "set" method

Data Binding does not Update on Property Change (UWP)

I am developing an application using c# and the Universal Windows Platform (UWP) and am struggling with creating a one-way data-bind between a layout control and an observable class. Currently, when the observable class property is changed, it does not update the UI element. I think it has something to do with the fact that I am binding a DataTemplate ListViewItem rather than a static layout element, but I am not sure if this is the problem or how to solve it. Any help would be appreciated. The code for the UI element and backend code is shown.
DataTemplate (XAML) (Styling is removed for readability)
<DataTemplate x:Key="variableTemplate"
x:DataType="local:VariableNode">
<Border>
<StackPanel Orientation="Vertical">
<Border>
<Grid>
<TextBlock Text="{Binding Name}" />
<StackPanel Orientation="Horizontal" >
<Button Tag="{Binding Description}"/>
<Button Tag="{Binding}"/>
</StackPanel>
</Grid>
</Border>
<Grid Margin="0, 10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Border >
<Grid Grid.Column="0">
<Button Click="Choose_Measurement"
Tag="{Binding}">
<StackPanel Orientation="Vertical">
<TextBlock Text="{x:Bind Path=Measurement_Name, Mode=TwoWay}"
Foreground="{x:Bind MF}" />
<TextBlock Foreground="{x:Bind MF}" />
</StackPanel>
</Button>
</Grid>
</Border>
<Grid Grid.Column="1">
<Button Foreground="{Binding UF}"
Tag="{Binding}"
IsEnabled="{Binding Unit_Exists}"
Click="Choose_Unit">
<StackPanel Orientation="Vertical">
<TextBlock Text="{x:Bind Path=Unit_Name, Mode=OneWay}"
Foreground="{Binding UF}" />
<TextBlock Foreground="{Binding UF}" />
</StackPanel>
</Button>
</Grid>
</Grid>
</StackPanel>
</Border>
</DataTemplate>
C# Observable Class VariableNode (Irrelevant properties removed)
public class VariableNode : ExperimentNode
{
public VariableNode() { }
public VariableNode(VariableType type)
{
Type = type;
Name = name_ref[(int)Type];
Category = "Problem";
Unit = -1;
}
private string[] name_ref = { "Independent Variable", "Dependent Variable", "Controlled Variable" };
public enum VariableType { Independent, Dependent, Controlled };
public VariableType Type { get; set; }
public Measurement Measure { get; set; }
public int Unit { get; set; }
[XmlIgnoreAttribute]
public Measurement MeasureSource
{
get { return this.Measure; }
set
{
this.Measure = value;
OnPropertyChanged("Measurement_Name");
}
}
[XmlIgnoreAttribute]
public string Measurement_Name
{
get
{
if (Measure == null) { return "Select a Measurement"; }
else { return Measure.Name; }
}
set
{
if (Measure != null)
{
Measure.Name = value;
OnPropertyChanged();
}
}
}
[XmlIgnoreAttribute]
public string Unit_Name
{
get
{
if (Measure == null) { return "No measurement"; }
else if (Unit < 0) { return "Select a unit"; }
else { return Measure.Unit[Unit]; }
}
}
[XmlIgnoreAttribute]
public bool Unit_Exists
{
get { return Measure != null; }
}
}
C# XAML.CS code calling the property change
public void Choose_Measurement (object sender, RoutedEventArgs e)
{
Button butt = sender as Button
VariableNode sel = butt.Tag as VariableNode;
sel.Measurement_Name = "New Name";
}
Again thanks for the help, I know its a lot of code, and I appreciate the help in debugging / learning.
Ok, so I ended up finding the answer, and I think that it may help others trying to replicate what I am trying to do:
Basically, the class that one is trying to make observable must extend the class INotifyPropertyChanged. So, I ended up making a base class from which to extend all of my observable classes from:
public class BaseClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged(this, e);
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
}

ItemsControl using WrapPanel, not displaying anything

Goal: I am trying to create a wrap panel that has children that are bound to an observable collection.
Current Expected Behavior: I expect to see 3 nested wrap panels that have an ellipse, a text block, a label and a checkbox.
Problem: My wrap panel and contents are not displayed at runtime. (Note: "Test" and "Test 2" Labels outside of the itemscontrol do display as expected.)
I have read this and it doesn't seem to solve my problem.
Code Behind
using MVVM_SandBox.Models;
using MVVM_SandBox.ViewModels;
namespace MVVM_SandBox
{
public partial class MainWindow
{
public MainViewModel VMMain = new MainViewModel();
public MainWindow()
{
VMMain.SomeItemModelBlahs = new System.Collections.ObjectModel.ObservableCollection<ItemModelBlah>() { new ItemModelBlah() { Label = "blah0" }, new ItemModelBlah() { Label = "blah1", CoolStuff = true }, new ItemModelBlah() { Label = "blah2" } };
InitializeComponent();
}
}
}
XAML
<Window x:Name="winMain" x:Class="MVVM_SandBox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewmodels="clr-namespace:MVVM_SandBox.ViewModels"
Title="MVVM SandBox" Height="600" Width="800" AllowDrop="True">
<Window.DataContext>
<viewmodels:MainViewModel></viewmodels:MainViewModel>
</Window.DataContext>
<StackPanel>
<Label Width="Auto" Height="Auto">Test</Label>
<ItemsControl ItemsSource="{Binding SomeItemModelBlahs}" Margin="10,10,10,10">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel x:Name="WrapPanelOfModelItems" Margin="10,10,10,10" Width="400" Height="200" IsItemsHost="True" MinWidth="200" MinHeight="200">
</WrapPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Width="100" Height="100" Margin="10">
<Ellipse Width="10" Height="10" Fill="Aqua"></Ellipse>
<TextBlock Margin="10" Text="{Binding Label}"></TextBlock>
<Label>kjasdkjalsdjklsad</Label>
<CheckBox IsChecked="{Binding CoolStuff}">
<Label>Hello</Label>
</CheckBox>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Label Height="Auto" Width="Auto">Test 2</Label>
</StackPanel>
</Window>
View Model
using MVVM_SandBox.Models;
using System.Collections.ObjectModel;
namespace MVVM_SandBox.ViewModels
{
//[ImplementPropertyChanged]
public class MainViewModel
{
public ObservableCollection<ItemModelBlah> SomeItemModelBlahs { get; set; }
public MainViewModel()
{
}
}
}
Model
namespace MVVM_SandBox.Models
{
public class ItemModelBlah
{
public string Label { get; set; }
public bool CoolStuff { get; set; }
}
}
The code is creating two instances of MainViewModel: once in the code behind, and again in the XAML. The code behind instance has a non-null ObservableCollection, while the instance in the XAML is set as the DataContext.
I would suggest removing the viewmodels:MainViewModel from the XAML, and creating it solely in code:
public partial class MainWindow
{
public MainViewModel VMMain = new MainViewModel();
public MainWindow()
{
DataContext = VWMain;
InitializeComponent();
}
}
Also, it's a better design to set up the ObservableCollection from within the view model itself:
class MainViewModel
{
public ObservableCollection<ItemModelBlah> SomeItemModelBlahs { get; private set; }
public MainViewModel()
{
SomeItemModelBlahs = new ObservableCollection<ItemModelBlah>()
{
new ItemModelBlah() { Label = "blah0" },
new ItemModelBlah() { Label = "blah1", CoolStuff = true },
new ItemModelBlah() { Label = "blah2" }
};
}
}

Why are my User Settings saved only the first time this method is called?

I have a WPF MVVM app, which gets its data from a user setting which is an ObservableCollection of type Copyable (a custom class) called Copyables. Within the main view model (ClipboardAssistantViewModel), I set the source of a CollectionViewSource to Copyables. This is then bound to an ItemsControl in the main view (MainWindow). The DataTemplate for this ItemsControl is a user control, 'CopyableControl', which is essentially a button, but with attached properties that allow me to bind data and commands to it.
When a user clicks on a CopyableControl, a view model (DefineCopyableViewModel) is added to an ObservableCollection of those in ClipboardAssistantViewModel, and that collection is bound to an ItemsControl in MainWindow. The DataTemplate of this is a UserControl called DefineCopyableControl, which is set up in such a way that the current values associated with the clicked Copyable are bound to textboxes in the DefineCopyableControl for editing.
My problem: There is a method in DefineCopyableViewModel, EditCopyable(), which only works on the first run (its job is to save the user settings once any edits have taken place and the user clicks "OK"). If I click the CopyableControl and make an edit, then click "OK", then click it again, make another edit, then click "OK", then close the application and open it again, only the first edit has been saved (even though the UI was updated with the edited value both times). It seems to have something to do with the data-binding need to be "refreshed"; please see the comments in this method in the code for my findings around this.
My code is as follows:
Model:
namespace ClipboardAssistant.Models
{
public class Copyable : INotifyPropertyChanged
{
// INotifyPropertyChanged implementation
private string name;
public string Name
{
get { return name; }
set
{
if (value != name)
{
name = value;
NotifyPropertyChanged("Name");
}
}
}
private string textToCopy;
public string TextToCopy
{
get { return textToCopy; }
set
{
if (value != textToCopy)
{
textToCopy = value;
NotifyPropertyChanged("TextToCopy");
}
}
}
public Copyable() { }
public Copyable(string Name, string TextToCopy)
{
this.Name = Name;
this.TextToCopy = TextToCopy;
}
}
}
ViewModels:
namespace ClipboardAssistant.ViewModels
{
public class ClipboardAssistantViewModel : INotifyPropertyChanged
{
// INotifyPropertyChanged Implementation
public CollectionViewSource CopyablesView { get; set; }
public ObservableCollection<DefineCopyableViewModel> Definers { get; set; }
public CopyableClickCommand CopyableClickCommand { get; set; }
public ClipboardAssistantViewModel()
{
Definers = new ObservableCollection<DefineCopyableViewModel>();
CopyablesView = new CollectionViewSource();
CopyablesView.Source = Properties.Settings.Default.Copyables;
CopyableClickCommand = new CopyableClickCommand(this);
EditModeClickCommand = new EditModeClickCommand(this);
}
public void RefreshCopyables()
{
// Both these methods of refreshing appear to have the same effect.
Properties.Settings.Default.Copyables = (ObservableCollection<Copyable>)CopyablesView.Source;
CopyablesView.Source = Properties.Settings.Default.Copyables;
}
public void EditCopyable(Copyable Copyable)
{
Definers.Add(new DefineCopyableViewModel(Copyable, this));
}
}
}
namespace ClipboardAssistant.ViewModels
{
public class DefineCopyableViewModel : INotifyPropertyChanged
{
// INotifyPropertyChanged Implementation
public ClipboardAssistantViewModel MyParent { get; set; }
public Copyable Copyable { get; set; }
public DefinerOKClickCommand DefinerOKClickCommand { get; set; }
public DefineCopyableViewModel(Copyable Copyable, ClipboardAssistantViewModel MyParent)
{
this.Copyable = Copyable;
this.MyParent = MyParent;
DefinerOKClickCommand = new DefinerOKClickCommand(this);
}
public void EditCopyable()
{
// Refresh, save, no refresh, save -> doesn't save second edit.
// Save, refresh, save, no refresh -> does save second edit.
MessageBoxResult r = MessageBox.Show("Refresh?", "Refresh", MessageBoxButton.YesNo);
if (r == MessageBoxResult.Yes)
{
MyParent.RefreshCopyables();
}
// These two MessageBox methods (save and refresh) can be swapped around (see above comments).
MessageBoxResult s = MessageBox.Show("Save?", "Save", MessageBoxButton.YesNo);
if (s == MessageBoxResult.Yes)
{
Properties.Settings.Default.Save();
}
MyParent.Definers.Remove(this);
}
}
}
MainWindow:
<Window x:Class="ClipboardAssistant.Views.MainWindow" x:Name="mainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:ClipboardAssistant.ViewModels"
xmlns:ctrls="clr-namespace:ClipboardAssistant.Controls"
mc:Ignorable="d"
Title="Clipboard Assistant" Height="400" Width="700">
<Window.DataContext>
<vm:ClipboardAssistantViewModel />
</Window.DataContext>
<Grid>
<Grid Margin="15">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="20" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<ItemsControl ItemsSource="{Binding CopyablesView.View}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ctrls:CopyableControl Copyable="{Binding}"
ClickCopyable="{Binding DataContext.CopyableClickCommand, ElementName=mainWindow}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<DockPanel Grid.Row="2">
<Button x:Name="btnEditCopyableMode" HorizontalAlignment="Left" DockPanel.Dock="Left"
Content="Edit" Margin="0,0,10,0" Command="{Binding EditModeClickCommand}" />
</DockPanel>
</Grid>
<ItemsControl ItemsSource="{Binding Definers}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ctrls:DefineCopyableControl Copyable="{Binding DataContext.Copyable}"
ClickCancel="{Binding DataContext.DefinerCancelClickCommand, ElementName=mainWindow}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
CopyableControl:
<UserControl x:Class="ClipboardAssistant.Controls.CopyableControl" x:Name="copyableControl"
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:ClipboardAssistant.Controls"
mc:Ignorable="d" d:DesignHeight="75" d:DesignWidth="200">
<Grid Width="200" Height="75">
<Button Command="{Binding ClickCopyable, ElementName=copyableControl}"
CommandParameter="{Binding Copyable, ElementName=copyableControl}"
Content="{Binding Copyable.Name, ElementName=copyableControl}"
Style="{StaticResource CopyableMainButtonStyle}" />
</Grid>
</UserControl>
namespace ClipboardAssistant.Controls
{
public partial class CopyableControl : UserControl
{
public static readonly DependencyProperty ClickCopyableProperty =
DependencyProperty.Register("ClickCopyable", typeof(ICommand), typeof(CopyableControl));
public ICommand ClickCopyable
{
get { return (ICommand)GetValue(ClickCopyableProperty); }
set { SetValue(ClickCopyableProperty, value); }
}
public static readonly DependencyProperty CopyableProperty =
DependencyProperty.Register("Copyable", typeof(Copyable), typeof(CopyableControl));
public Copyable Copyable
{
get { return (Copyable)GetValue(CopyableProperty); }
set { SetValue(CopyableProperty, value); }
}
public CopyableControl()
{
InitializeComponent();
}
}
}
DefineCopyableControl:
<UserControl x:Class="ClipboardAssistant.Controls.DefineCopyableControl" x:Name="defineCopyableControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="500">
<Grid x:Name="MainGrid" Background="Blue">
<Grid Width="200" Height="180">
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="10" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="20" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<Label Grid.Row="0" Content="Name" Foreground="White" />
<TextBox Grid.Row="1" Text="{Binding Copyable.Name}" x:Name="tbN" />
<Label Grid.Row="3" Content="Copyable Text" Foreground="White" />
<TextBox Grid.Row="4" Text="{Binding Copyable.TextToCopy}" x:Name="tbTTC" />
<DockPanel Grid.Row="6">
<Button Width="70" Content="OK" DockPanel.Dock="Right" HorizontalAlignment="Right"
Command="{Binding DefinerOKClickCommand}"
CommandParameter="{Binding ElementName=defineCopyableControl}" />
</DockPanel>
</Grid>
</Grid>
</UserControl>
public partial class DefineCopyableControl : UserControl
{
public static readonly DependencyProperty CopyableProperty =
DependencyProperty.Register("Copyable", typeof(Copyable), typeof(DefineCopyableControl));
public Copyable Copyable
{
get { return (Copyable)GetValue(CopyableProperty); }
set { SetValue(CopyableProperty, value); }
}
public DefineCopyableControl()
{
InitializeComponent();
}
}
Commands:
namespace ClipboardAssistant.ViewModels.Commands
{
public class CopyableClickCommand : ICommand
{
public ClipboardAssistantViewModel ViewModel { get; set; }
public CopyableClickCommand(ClipboardAssistantViewModel viewModel)
{
ViewModel = viewModel;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
Copyable cp = (Copyable)parameter;
ViewModel.EditCopyable(cp);
}
}
}
namespace ClipboardAssistant.ViewModels.Commands
{
public class DefinerOKClickCommand : ICommand
{
public DefineCopyableViewModel ViewModel { get; set; }
public DefinerOKClickCommand(DefineCopyableViewModel viewModel)
{
ViewModel = viewModel;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
ViewModel.EditCopyable();
}
}
}
Settings:
namespace ClipboardAssistant.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public global::System.Collections.ObjectModel.ObservableCollection<ClipboardAssistant.Models.Copyable> Copyables {
get {
return ((global::System.Collections.ObjectModel.ObservableCollection<ClipboardAssistant.Models.Copyable>)(this["Copyables"]));
}
set {
this["Copyables"] = value;
}
}
}
}
I'm assuming you are using Visual Studio. In that case, in the My Project do you have the settings listed in the settings tab?
I ran into the same issue a while back where I tried to programatically create/save/update settings and was unsucessful until I created the setting in the Settings tab. Once that was complete I was able to make my saves as necessary.
The you just use
MySettings.Default.SettingName = value
MySettings.Default.Save()
Hope this helps!

DataTemplate is not generating ListItemBox

I want to generate ListItemBox using DataTemplate but items are not generating. Please guide me where is the mistake. I have following code in MainWindow.xaml.
<Window x:Class="Offline_Website_Downloader.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:bd="clr-namespace:Offline_Website_Downloader"
Title="Offline Website Downloader" Background="#f5f6f7" Height="500" Width="800"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<bd:BindingController x:Key="BindingControllerKey" />
<DataTemplate x:Key="DownloadedWebsitesListBox">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal" Width="Auto">
<TextBlock FontWeight="Bold" FontSize="18" Width="480">
<Hyperlink NavigateUri="http://google.com">
<Label Content="{Binding Path=WebsiteTitle}" />
</Hyperlink>
</TextBlock>
<TextBlock Width="132" TextAlignment="right">
<TextBlock Text="Remaining Time: "/>
<TextBlock Name="TimeRemaining" Text="js"/>
</TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal">
<ProgressBar Name="progress1" Maximum="100" Minimum="0" Value="30" Background="#FFF" Width="612" Height="10" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock HorizontalAlignment="Left" Width="450">Status: <TextBlock Text="{Binding Path=Status}"/></TextBlock>
<TextBlock Width="162" TextAlignment="right">
<TextBlock Text="Downloading Speed: "/>
<TextBlock Name="DownloadingSpeed" Text="{Binding Path=DownloadingSpeed}"/>
</TextBlock>
</StackPanel>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox Width="Auto"
Name="WebsiteList"
Grid.Column="1"
Grid.Row="2"
Grid.RowSpan="2"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource DownloadedWebsitesListBox}"
Margin="0,0,0,0">
</ListBox>
</Grid>
</window>
and MainWindow.xaml.cs
BindingController bc = new BindingController();
public MainWindow()
{
InitializeComponent();
bc.DownloadingSpeed = "40kb/s";
bc.WebsiteTitle = "WebsiteTitle";
bc.Status = "Downloading";
DataContext = bc;
}
and BindingController.cs
public class BindingController
{
public BindingController()
{
}
private string _WebsiteTitle;
public string WebsiteTitle
{
set { _WebsiteTitle = value; }
get { return _WebsiteTitle; }
}
private string _Status;
public string Status
{
set { _Status = value ; }
get { return _Status ; }
}
private string _DownloadStartDate;
public string DownloadStartDate
{
set { _DownloadStartDate = value; }
get { return _DownloadStartDate ; }
}
private string _DownloadingSpeed = "0 kb/s";
public string DownloadingSpeed
{
set { _DownloadingSpeed = value; }
get { return _DownloadingSpeed; }
}
}
Your problem is that you're binding a ListBox to an object that contains several properties, but really only represents a single object/state. The ListBox expects to display a list of items (i.e. IList, IBindingList, IEnumerable, ObservableCollection).
Assuming you want to display more than one download at a time, which I'm assuming given that you're using a ListBox, I would refactor the download properties into a separate class. You will also need to implement INotifyPropertyChanged on your properties so that when the values are changed, they will be shown in the UI.
public class Download : INotifyPropertyChanged
{
private string _WebsiteTitle;
public string WebsiteTitle
{
get { return _WebsiteTitle; }
set
{
if (_WebsiteTitle == value)
return;
_WebsiteTitle = value;
this.OnPropertyChanged("WebsiteTitle");
}
}
private string _Status;
public string Status
{
get { return _Status; }
set
{
if (_Status == value)
return;
_Status = value;
this.OnPropertyChanged("Status");
}
}
private string _DownloadStartDate;
public string DownloadStartDate
{
get { return _DownloadStartDate; }
set
{
if (_DownloadStartDate == value)
return;
_DownloadStartDate = value;
this.OnPropertyChanged("DownloadStartDate");
}
}
private string _DownloadingSpeed = "0 kb/s";
public string DownloadingSpeed
{
get { return _DownloadingSpeed; }
set
{
if (_DownloadingSpeed == value)
return;
_DownloadingSpeed = value;
this.OnPropertyChanged("DownloadingSpeed");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if(this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
The new BindingController:
public class BindingController
{
public BindingController()
{
this.Downloads = new ObservableCollection<Download>();
}
public ObservableCollection<Download> Downloads { get; private set; }
}
Setting up the bindings in XAML:
<ListBox Width="Auto"
Name="WebsiteList"
Grid.Column="1"
Grid.Row="2"
Grid.RowSpan="2"
ItemsSource="{Binding Downloads}"
ItemTemplate="{StaticResource DownloadedWebsitesListBox}"
Margin="0,0,0,0">
</ListBox>
Initializing the collection in MainWindow
Download download = new Download();
download.DownloadingSpeed = "40kb/s";
download.WebsiteTitle = "WebsiteTitle";
download.Status = "Downloading";
bc.Downloads.Add(download);
this.DataContext = bc;

Categories