I have no Binding errors in my output window.
I want to bind text from a file(s) to one/many avalonEdit controls via MVVM.
How can I do that, because its not working the way I tried?!
VIEW
>
<Window.Resources>
<DataTemplate x:Key="NormalTemplate">
<Expander Margin="0" Header="{Binding FileName}">
<!--<TextBox TextWrapping="Wrap" AcceptsReturn="True" IsReadOnly="True" Text="{Binding Content}" Margin="0"/>-->
<avalonEdit:TextEditor Document="{Binding Document, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></avalonEdit:TextEditor>
</Expander>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40px"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid Background="Orange">
<StackPanel Orientation="Horizontal">
<Button>Run</Button>
<Button Command="{Binding SaveCommand}">Save</Button>
</StackPanel>
</Grid>
<Grid Grid.Row="1">
<ScrollViewer ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ItemsControl ItemsSource="{Binding ErrorLogs}" HorizontalContentAlignment="Stretch" ItemTemplate="{DynamicResource NormalTemplate}" />
</ScrollViewer>
</Grid>
</Grid>
</Window>
VIEWMODEL
public class ErrorLogViewModel : BaseViewModel
{
public string FileName { get; set; }
public string Content { get; set; }
private TextDocument _document = null;
public TextDocument Document
{
get { return this._document; }
set
{
if (this._document != value)
{
this._document = value;
RaisePropertyChanged("Document");
}
}
}
}
public class MainViewModel : BaseViewModel
{
public MainViewModel()
{
GetErrorLogs();
SaveCommand = new RelayCommand(Save);
}
public RelayCommand SaveCommand { get; private set; }
private void Save()
{
var text = ErrorLogs[0].Document.Text;
// I get at least the Original loaded text
}
private void GetErrorLogs()
{
for (int i = 0; i < 30; i++)
{
using (FileStream fs = new FileStream("text.txt", FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (StreamReader reader = FileReader.OpenStream(fs, Encoding.UTF8))
{
var doc = new TextDocument(reader.ReadToEnd());
var e = new ErrorLogViewModel { FileName = "ErrorLog " + i, Document = doc };
ErrorLogs.Add(e);
}
}
}
}
private ObservableCollection<ErrorLogViewModel> _errorLogs = new ObservableCollection<ErrorLogViewModel>();
public ObservableCollection<ErrorLogViewModel> ErrorLogs
{
get { return _errorLogs; }
set
{
_errorLogs = value;
this.RaisePropertyChanged("ErrorLogs");
}
}
}
I have looked at that sample were MVVM is introduced:
http://www.codeproject.com/Articles/570313/AvalonDock-Tutorial-Part-AvalonEdit-in-Avalo
I do the same binding actually when I load a text file but my text is just not shown :/
You can find here a repro visual studio solution:
http://www.file-upload.net/download-9067428/ExpanderTest.7z.html
Related
The problem might look similar to this
But I don't have an issue with updating the TreeView if the ObservableCollection is modified in the same window (as in the case of the linked post).
I have a TreeView located in my MainWindow. I use a HierarchicalDataTemplate for the TreeView, basically the TreeView is structured into three levels of hierarchy: Group, Report, Node
For each level of the TreeView, I have created simple Model classes, each class will have a Name property and an ObservableCollection of its children class as second property (e.g., Group class will have a Name and a ReportList property, whilst Report class will have a Name and NodeList property)
In the ViewModel of my MainWindow, I have an ObservableCollection of the Group objects (I named it Groups) that is bound to the TreeView. I also have a RelayCommand method that will add a new Group object to the Groups collection (Let's call it the ImportReport Method). If I bind the RelayCommand to the button inside the MainWindow, everything worked fine, the TreeView is updated immediately with new Group Object each time I press the button.
However, this becomes an issue when I have another window, let's call it ImportWindow. In ImpportWindow, I have a button. Probably this is the cause of the issue, but I use the same ViewModel for both the ImportWindow and the MainWindow so that they can share the Groups property that is bound to the TreeView in the MainWindow. My goal is to perform addition to the TreeView from the second window by pressing a button from there. Now, If I move the binding of the ImportReport command to the button in ImportWindow instead, then try to run the code, pressing the button on ImportWindowdoesn't update the TreeView at all. I have debugged the code and the Groups ObservableCollection in the ViewModel indeed recorded a new addition but the TreeView doesn't display the new Group object.
Any explanation to this? Thanks!
MainWindow
<Window x:Class="StressReportWPF.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:local="clr-namespace:StressReportWPF"
xmlns:ribbon="clr-namespace:System.Windows.Controls.Ribbon;assembly=System.Windows.Controls.Ribbon"
xmlns:viewModel="clr-namespace:StressReportWPF.ViewModel"
xmlns:data="clr-namespace:StressReportWPF.DataElements"
mc:Ignorable="d"
Title="StressReportApp"
Height="750"
Width="1000">
<Window.DataContext>
<viewModel:MainViewModel/>
</Window.DataContext>
<Window.Resources>
<HierarchicalDataTemplate DataType="{x:Type data:DisplayGroup}"
ItemsSource="{Binding ReportList}">
<TextBlock Text="{Binding DisplayGroupName}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type data:DisplayReport}"
ItemsSource="{Binding NodeList}">
<TextBlock Text="{Binding DisplayReportName}"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type data:DisplayNode}">
<TextBlock Text="{Binding DisplayNodeName}"/>
</DataTemplate>
</Window.Resources>
<Border Background="#e7f4fe">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="75"/>
<RowDefinition/>
</Grid.RowDefinitions>
...
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0"
Margin="0,20,20,20"
Background="#dde6ed"
BorderBrush="Black"
BorderThickness="1">
<ScrollViewer>
<TreeView Background="Transparent"
ItemsSource="{Binding Groups, UpdateSourceTrigger=PropertyChanged}">
</TreeView>
</ScrollViewer>
</Border>
<Border Grid.Column="1"
Margin="20,20,0,20"
Background="White"
BorderBrush="Black"
BorderThickness="1">
<ContentControl Margin="0"
Content="{Binding CurrentView}">
</ContentControl>
</Border>
</Grid>
</Grid>
</Border>
ImportWindow
<Window x:Class="StressReportWPF.ImportWindow"
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:local="clr-namespace:StressReportWPF"
xmlns:viewModel="clr-namespace:StressReportWPF.ViewModel"
xmlns:data="clr-namespace:StressReportWPF.DataElements"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
mc:Ignorable="d"
Title="ImportWindow" Height="450" Width="800"
Background="#dde6ed">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisConverter"/>
<DataTemplate x:Key="GroupComboBoxTemplate">
<TextBlock Text="{Binding DisplayGroupName}"/>
</DataTemplate>
</Window.Resources>
<Window.DataContext>
<viewModel:MainViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="500"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
...
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="200"/>
<RowDefinition Height="150"/>
<RowDefinition/>
</Grid.RowDefinitions>
...
<Button Grid.Row="2"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Height="40"
Width="100"
Content="Import"
Style="{StaticResource StandardButtonStyle}"
Command="{Binding ImportCommand}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:CallMethodAction MethodName="Close"
TargetObject="{Binding RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType=Window}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</Grid>
</Grid>
ViewModel
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using StressReportWPF.Core;
using StressReportWPF.DataElements;
using System.Collections.ObjectModel;
using System.Windows.Input;
using StressReportWPF.Views;
namespace StressReportWPF.ViewModel
{
public class MainViewModel: ObservableObject
{
#region ViewModel-related Properties
public RelayCommand HomePageViewCommand { get; set; }
public RelayCommand LoadCaseAssignmentViewCommand { get; set; }
public RelayCommand JointNumberAssignmentViewCommand { get; set; }
public HomePageViewModel HomePageVM { get; set; }
public LoadCaseAssignmentViewModel LoadCaseAssignmentVM { get; set; }
public JointNumberAssignmentViewModel JointNumberAssignmentVM { get; set; }
private object _currentView;
public object CurrentView
{
get { return _currentView; }
set
{
_currentView = value;
OnPropertyChanged();
}
}
#endregion
private ICommand _importCommand;
public ICommand ImportCommand
{
get
{
if (_importCommand == null)
{
_importCommand = new RelayCommand(
param => ImportReport()
);
}
return _importCommand;
}
}
private ICommand _importWindowCommand;
public ICommand ImportWindowCommand
{
get
{
if (_importWindowCommand == null)
{
_importWindowCommand = new RelayCommand(
param => ShowImportWindow()
);
}
return _importWindowCommand;
}
}
#region Data Properties
private readonly ObservableCollection<DisplayGroup> _groups = new ObservableCollection<DisplayGroup>();
public ObservableCollection<DisplayGroup> Groups
{
get { return _groups; }
}
public bool IsNewGroupCreated { get; set; }
public object SelectedGroup { get; set; }
private DisplayGroup _newGroup = new DisplayGroup();
public DisplayGroup NewGroup
{
get { return _newGroup; }
set
{
_newGroup = value;
OnPropertyChanged();
}
}
#endregion
public MainViewModel()
{
HomePageVM = new HomePageViewModel();
LoadCaseAssignmentVM = new LoadCaseAssignmentViewModel();
JointNumberAssignmentVM = new JointNumberAssignmentViewModel();
CurrentView = HomePageVM;
HomePageViewCommand = new RelayCommand(o =>
{
CurrentView = HomePageVM;
});
LoadCaseAssignmentViewCommand = new RelayCommand(o =>
{
CurrentView = LoadCaseAssignmentVM;
});
JointNumberAssignmentViewCommand = new RelayCommand(o =>
{
CurrentView = JointNumberAssignmentVM;
});
// Set a temporary Group list
List<DisplayGroup> testGroupList = new List<DisplayGroup>();
List<DisplayNode> testNodeList = new List<DisplayNode>();
List<DisplayReport> testReportList = new List<DisplayReport>();
DisplayNode nodeA = new DisplayNode();
nodeA.DisplayNodeName = "Node A";
DisplayNode nodeB = new DisplayNode();
nodeB.DisplayNodeName = "Node B";
DisplayNode nodeC = new DisplayNode();
nodeC.DisplayNodeName = "Node C";
testNodeList.AddRange(new List<DisplayNode> { nodeA, nodeB, nodeC }) ;
ObservableCollection<DisplayNode> testNodeObsCol = new ObservableCollection<DisplayNode>(testNodeList);
DisplayReport reportA = new DisplayReport();
reportA.DisplayReportName = "Report A";
reportA.NodeList = testNodeObsCol;
DisplayReport reportB = new DisplayReport();
reportB.DisplayReportName = "Report B";
reportB.NodeList = testNodeObsCol;
DisplayReport reportC = new DisplayReport();
reportC.DisplayReportName = "Report C";
reportC.NodeList = testNodeObsCol;
testReportList.AddRange(new List<DisplayReport> { reportA, reportB, reportC });
ObservableCollection<DisplayReport> testReportObsCol = new ObservableCollection<DisplayReport>(testReportList);
DisplayGroup groupA = new DisplayGroup();
groupA.DisplayGroupName = "Group A";
groupA.ReportList = testReportObsCol;
DisplayGroup groupB = new DisplayGroup();
groupB.DisplayGroupName = "Group B";
groupB.ReportList = testReportObsCol;
DisplayGroup groupC = new DisplayGroup();
groupC.DisplayGroupName = "Group C";
testGroupList.AddRange(new List<DisplayGroup> { groupA, groupB, groupC });
foreach (var item in testGroupList)
{
Groups.Add(item);
}
}
public void ShowImportWindow()
{
//ImportViewModel importVM = new ImportViewModel();
//importVM.Groups = this.Groups;
ImportWindow importWindow = new ImportWindow();
//importWindow.DataContext = importVM;
importWindow.Show();
}
public void ImportReport()
{
if (IsNewGroupCreated)
{
DisplayGroup newGroup = new DisplayGroup();
newGroup.DisplayGroupName = "Group D";
Groups.Add(newGroup);
}
else
{
}
}
}
}
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>
I have two Controls on a single Page :
1. RadSlider
2. ListBox
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="100"></RowDefinition>
</Grid.RowDefinitions>
<telerik:RadSlideView Name="imgSlidView" >
<telerik:RadSlideView.ItemTemplate>
<DataTemplate>
<Image Source="{Binding Src}"></Image>
</DataTemplate>
</telerik:RadSlideView.ItemTemplate>
<telerik:RadSlideView.ItemPreviewTemplate>
<DataTemplate>
<telerik:RadBusyIndicator></telerik:RadBusyIndicator>
</DataTemplate>
</telerik:RadSlideView.ItemPreviewTemplate>
</telerik:RadSlideView>
<ListBox Grid.Row="1" Name="lstImage">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Image Height="100" Margin="0,0,5,0" Source="{Binding Src}"></Image>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I want to bind the two controls to a single Item Source such that if selection of one changes other's selection also should change . I am interested in MVVM based solution.
My code :
class CategoryViewModel : ViewModelBase
{
public ObservableCollection<ImageSource> ImageCollection { get; set; }
private ImageSource _CurrentImage;
public ImageSource CurrentImage
{
get { return _CurrentImage; }
set
{
_CurrentImage = value;
RaisePropertyChanged("CurrentImage");
}
}
}
In addition to this I have a piece of code that returns IEnumerable and I want this to be as Item Source.
public static async Task<IEnumerable<Object>> GetCategoryNames()
{
if (Categories == null)
{
JDir dir = Newtonsoft.Json.JsonConvert.DeserializeObject<JDir>(await LoadFromJson());
Categories = ConvertJDirToCategory(dir);
return Categories.Select(p => new { Name = p.Name, Src = "Images/" + p.Name + ".jpg" });
}
else
{
return Categories.Select(p => new { Name = p.Name, Src = "Images/" + p.Name + ".jpg" });
}
}
Am I doing in right way ? How should I proceed ?
Thanks in advance !
EDIT - from comments:
private static async Task<string> LoadFromJson()
{
string theData = string.Empty;
StorageFile file = await Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///data.json"));
using (StreamReader streamReader = new StreamReader(await file.OpenStreamForReadAsync()))
{
return await streamReader.ReadToEndAsync();
}
}
Thanks every body, My problem has been solved now :
XAML :
DataContext="{Binding Category, Source={StaticResource Locator}}"
Two Controls :
<telerik:RadSlideView Name="imgSlidView" SelectedItem="{Binding SelectedItem,Mode=TwoWay}" ItemsSource="{Binding Images}">
<telerik:RadSlideView.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}">
<telerik:RadContextMenu.ContextMenu>
<telerik:RadContextMenu IsZoomEnabled="False" OpenGesture="Tap">
<telerik:RadContextMenuItem Tap="RadContextMenuItem_Tap" Content="Share">
</telerik:RadContextMenuItem>
</telerik:RadContextMenu>
</telerik:RadContextMenu.ContextMenu>
</Image>
</DataTemplate>
</telerik:RadSlideView.ItemTemplate>
<telerik:RadSlideView.ItemPreviewTemplate>
<DataTemplate>
<telerik:RadBusyIndicator></telerik:RadBusyIndicator>
</DataTemplate>
</telerik:RadSlideView.ItemPreviewTemplate>
</telerik:RadSlideView>
<ListBox Grid.Row="1" ScrollViewer.HorizontalScrollBarVisibility="Auto" Name="lstImage" SelectedItem="{Binding SelectedItem,Mode=TwoWay}" ItemsSource="{Binding Images}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Image Height="100" Margin="0,0,5,0" Source="{Binding}">
</Image>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
View Model :
public class CategoryViewModel : ViewModelBase
{
private string _CategoryName;
public string CategoryName
{
get { return _CategoryName; }
set
{
DispatcherHelper.CheckBeginInvokeOnUI(() => { Set<string>(ref _CategoryName, value); });
}
}
private Uri _SelectedItem;
public Uri SelectedItem
{
get { return _SelectedItem; }
set
{
DispatcherHelper.CheckBeginInvokeOnUI(() => { Set<Uri>(ref _SelectedItem, value); });
}
}
private ObservableCollection<Uri> _Images;
public ObservableCollection<Uri> Images
{
get { return _Images; }
set { Set<ObservableCollection<Uri>>(ref _Images, value); }
}
public CategoryViewModel()
{
CategoryName = string.Empty;
Images = new ObservableCollection<Uri>();
}
}
XAML.cs
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
string Category = string.Empty;
NavigationContext.QueryString.TryGetValue("category", out Category);
if (this.DataContext is CategoryViewModel)
{
var vm = (CategoryViewModel)this.DataContext;
vm.Images.Clear();
JSONHelper.LoadFromJson().ContinueWith(t =>
{
vm.CategoryName = Category;
var images = t.Result.Dirs.FirstOrDefault(p => p.DirName == Category).Files;
Dispatcher.BeginInvoke(() =>
{
foreach (var img in images)
{
vm.Images.Add(new Uri(string.Format("Data/{0}/{1}", Category, img), UriKind.Relative));
}
});
});
}
}
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;
I've been working on this problem for about 3 hours now, and I got to a dead end.
Currently I'm trying to bind a list to a ComboBox.
I have used several methods to bind the List:
Code behind:
public partial class MainWindow : Window
{
public coImportReader ir { get; set; }
public MainWindow()
{
ir = new coImportReader();
InitializeComponent();
}
private void PremadeSearchPoints(coSearchPoint sp)
{
//SearchRefPoint.DataContext = ir.SearchPointCollection;
SearchRefPoint.ItemsSource = ir.SearchPointCollection;
SearchRefPoint.DisplayMemberPath = Name;
The data was binded correctly but the DisplayMemeberPath for some reason returned the name of the class and not the name of it's member.
The XAML method returned an empty string...
<ComboBox x:Name="SearchRefPoint" Height="30" Width="324" Margin="0,10,0,0"
VerticalAlignment="Top" ItemsSource="{Binding ir.SearchPointCollection}"
DisplayMemberPath="Name">
I've also tried to fill it with a new list which I create in the MainWindow. the result was the same in both cases.
Also I've tried to create and ListCollectionView which was success, but the problem was that I could get the index of the ComboBox item. I prefer to work by an Id. For that reason I was looking for a new solution which I found at: http://zamjad.wordpress.com/2012/08/15/multi-columns-combo-box/
The problem with this example is that is not clear how the itemsource is being binded.
Edit:
To sum things up: I'm currently trying to bind a list(SearchPointsCollection) of objects(coSearchPoints) defined in a class (coImportReader).
namespace Import_Rates_Manager
{
public partial class MainWindow : Window
{
public coImportReader ir;
public coViewerControles vc;
public coSearchPoint sp;
public MainWindow()
{
InitializeComponent();
ir = new coImportReader();
vc = new coViewerControles();
sp = new coSearchPoint();
SearchRefPoint.DataContext = ir;
}
}
}
//in function....
SearchRefPoint.ItemsSource = ir.SearchPointCollection;
SearchRefPoint.DisplayMemberPath = "Name";
namespace Import_Rates_Manager
{
public class coImportReader
{
public List<coSearchPoint> SearchPointCollection = new List<coSearchPoint>();
}
}
namespace Import_Rates_Manager
{
public class coSearchPoint
{
public coSearchPoint()
{
string Name = "";
Guid Id = Guid.NewGuid();
IRange FoundCell = null;
}
}
}
This results in a filled combobox with no text
Here a simple example using the MVVM Pattern
XAML
<Window x:Class="Binding_a_List_to_a_ComboBox.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 HorizontalAlignment="Left"
VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="25"/>
</Grid.RowDefinitions>
<ComboBox Grid.Column="0" Grid.Row="0" ItemsSource="{Binding SearchPointCollection , UpdateSourceTrigger=PropertyChanged}"
SelectedIndex="{Binding MySelectedIndex, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding MySelectedItem, UpdateSourceTrigger=PropertyChanged}">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding Id}" Grid.Row="0"/>
<TextBlock Text="{Binding Name}" Grid.Row="1"/>
<TextBlock Text="{Binding Otherstuff}" Grid.Row="2"/>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Content="Bind NOW" Grid.Column="0" Grid.Row="1" Click="Button_Click"/>
<TextBlock Text="{Binding MySelectedIndex, UpdateSourceTrigger=PropertyChanged}" Grid.Column="1" Grid.Row="0"/>
<Grid Grid.Column="1" Grid.Row="1"
DataContext="{Binding MySelectedItem, UpdateSourceTrigger=PropertyChanged}">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Id}" Grid.Column="0"/>
<TextBlock Text="{Binding Name}" Grid.Column="1"/>
<TextBlock Text="{Binding SomeValue}" Grid.Column="2"/>
</Grid>
</Grid>
Code
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using Import_Rates_Manager;
namespace Binding_a_List_to_a_ComboBox
{
/// <summary>
/// Interaktionslogik für MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
DataContext = new coImportReader();
}
}
}
namespace Import_Rates_Manager
{
public class coImportReader : INotifyPropertyChanged
{
private List<coSearchPoint> myItemsSource;
private int mySelectedIndex;
private coSearchPoint mySelectedItem;
public List<coSearchPoint> SearchPointCollection
{
get { return myItemsSource; }
set
{
myItemsSource = value;
OnPropertyChanged("SearchPointCollection ");
}
}
public int MySelectedIndex
{
get { return mySelectedIndex; }
set
{
mySelectedIndex = value;
OnPropertyChanged("MySelectedIndex");
}
}
public coSearchPoint MySelectedItem
{
get { return mySelectedItem; }
set { mySelectedItem = value;
OnPropertyChanged("MySelectedItem");
}
}
#region cTor
public coImportReader()
{
myItemsSource = new List<coSearchPoint>();
myItemsSource.Add(new coSearchPoint { Name = "Name1" });
myItemsSource.Add(new coSearchPoint { Name = "Name2" });
myItemsSource.Add(new coSearchPoint { Name = "Name3" });
myItemsSource.Add(new coSearchPoint { Name = "Name4" });
myItemsSource.Add(new coSearchPoint { Name = "Name5" });
}
#endregion
#region INotifyPropertyChanged Member
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
public class coSearchPoint
{
public Guid Id { get; set; }
public String Name { get; set; }
public IRange FoundCell { get; set; }
public coSearchPoint()
{
Name = "";
Id = Guid.NewGuid();
FoundCell = null;
}
}
public interface IRange
{
string SomeValue { get; }
}
}
Here are 3 Classes:
MainWindow which set VM as his Datacontext
coImportReader the Class which presents your properties for your bindings
coSearchPoint which is just a Container for your information
IRange which is just an Interface
The Collection you are binding to needs to be a property of ir not a field.
Also try this :
public coImportReader ir { get; set; }
public <type of SearchPointCollection> irCollection { get { return ir != null ? ir.SearchPointCollection : null; } }
Bind to irCollection and see what errors you get if any.
The DisplayMemberPath should contain the property name of the elements in your collection. Assuming the elements in the SearchPointCollection are of the type SearchPoint and this class has a Property SearchPointName you should set DisplayMemberPath like this:
SearchRefPoint.DisplayMemberPath = "SearchPointName";
Edit:
In your code the class coSearchPoint has the Field Name defined in the Constructor. Name has to be a Property of the class, otherwise the Binding can't work.