MVVM access Richtextbox flowdocument from VM - c#

I am trying to make a RichTextBox with a FlowDocument that I can insert text at the caret position. I can add text to the end of the document. I think I am missing something in my setup to that allows my VM to access the Flowocument or I am setting it up wrong. If I create a FlowDocument in my VM and try to set my RichTextBox to it I get an error that my MyEditor (RichTextBox) does not exist. I can add text to the RichTextBox using what I call the AddItemBtn from a ListBox so at least that much works.
My question is "How should I set my RichTextBox/FlowDocument up?
XAML code
<Window x:Class="Scripter.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Scripter.ViewModels"
xmlns:wpftoolkit="clr-namespace:Xceed.Wpf.Toolkit;assembly=Xceed.Wpf.Toolkit"
Title="MainWindow" Height="350" Width="725">
<Grid HorizontalAlignment="Stretch">
<Grid HorizontalAlignment="Stretch" Height="72" Margin="10,14,0,0" VerticalAlignment="Top" Width="auto">
<WrapPanel HorizontalAlignment="Left" Height="50" Margin="10,0,0,0" VerticalAlignment="Top">
</WrapPanel>
<Button x:Name="OpenFilesBtn" Content="Open" HorizontalAlignment="Left" Margin="15,10,0,0" VerticalAlignment="Top" Width="75" Command="{Binding OpenFileBtn}"/>
<Button x:Name="SavefilesBtn" Content="Save" HorizontalAlignment="Left" Margin="104,10,0,0" VerticalAlignment="Top" Width="75" Command="{Binding SaveFileBtn}"/>
<TextBlock x:Name="OpenFile" Text="{Binding OpenFile,Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Margin="15,37,0,0" VerticalAlignment="Top" Width="353"/>
<ComboBox x:Name="TipsBtn" SelectedIndex="0" ItemsSource="{Binding Path=Tabs, UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding Path=SelectedOption}" HorizontalAlignment="Left" Margin="538,10,0,0" VerticalAlignment="Top" Width="120"/>
<Button x:Name="AddItemBtn" Content="Add Item" HorizontalAlignment="Left" Margin="417,10,0,0" VerticalAlignment="Top" Width="100" Command="{Binding AddItemBtn}" CommandParameter="{Binding ElementName=AddItemList,Path=SelectedItem}"/>
</Grid>
<Grid Margin="10,100,10,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<RichTextBox Grid.Column="0" x:Name="MyEditor" SelectionChanged="MyEditor_SelectionChanged" ScrollViewer.VerticalScrollBarVisibility="Auto" Margin="0" Height="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Width="Auto" IsDocumentEnabled="True" AcceptsTab="True" AcceptsReturn="True" >
<RichTextBox.Resources>
<Style TargetType="{x:Type Paragraph}">
<Setter Property="Margin" Value="0" ></Setter>
<Setter Property="FontSize" Value="15"></Setter>
</Style>
</RichTextBox.Resources>
<FlowDocument >
<Paragraph >
<Run Text="{Binding TestText}" ></Run>
</Paragraph>
</FlowDocument>
</RichTextBox>
<ListBox x:Name="AddItemList" Grid.Column="1" Width="Auto" Height="Auto" ItemsSource="{Binding Path=OptionsToChoose}" SelectedItem="ItemSelected">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock x:Name="TextSelected" Text="{Binding Description}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
VM code that has the issue
public ScripterViewModel()
{
ScripterModel scripterModel = new ScripterModel();
ObservableCollection<string> tabsChoice = new ObservableCollection<string>();
tabsChoice.Add("Tabs");
tabsChoice.Add("Buttons");
Tabs = tabsChoice;
this.OpenFileBtn = new DelegateCommand(chooseFile, canChooseFile).ObservesProperty(() => OpenFile);
this.SaveFileBtn = new DelegateCommand(saveFile, canSaveFile).ObservesProperty(() => SaveFile);
this.AddItemBtn = new DelegateCommand<Tabbed>(addItem);
FlowDocument flowDoc = new FlowDocument();
Paragraph p = new Paragraph(new Run("new paragraph"));
flowDoc.Blocks.Add(new Paragraph(new Run("Paragraph 1")));
flowDoc.Blocks.Add(p);
//MyEditor = flowDoc;
}
public void MyEditor_SelectionChanged(object sender, RoutedEventArgs e)
{
// TextRange tempRange = new TextRange(MyEditor.Document.ContentStart, MyEditor.Selection.Start);
MessageBox.Show("Selection Changed");
}
private string _testText;
public string TestText
{
get
{
return _testText;
}
set
{
string _temp;
_temp = _testText + value;
SetProperty(ref _testText, value);
}
}

Hey I am new to WPF and MVVM but I'll give my best to help you. So don't blame me if I'm wrong.
1. Set Window.DataContext
First of all you have to tell your View where it can get the data from.
This can be done by adding this code to your View.xaml:
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
But make sure your namespace variable (here "local") points to your ViewModels.
xmlns:local="clr-namespace:Client.ViewModel"
This for ex. points to my ViewModel folder.
2. Define a OnPropertyChanged method
Your View won't know if you have modified a variable. So you need a method to notify your View about the changes.
First of all implement the interface INotifyPropertyChanged to your ViewModel.
public class MainViewModel : ViewModelBase, INotifyPropertyChanged
Now add this code:
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
3. Use OnPropertyChanged
So now you have a method to tell your View that a variable has changed but how do you use it?
To explain this to you I'll use your FlowDocument flowDoc.
So let's begin by defining your FlowDocument setting up :
private FlowDocument _flowDoc;
Now lets write a getter & setter for flowDoc:
public FlowDocument FlowDoc
{
get
{
return _flowDoc;
}
set
{
_flowDoc = value;
}
}
Now it's time to use our OnPropertyChanged method which we created in 2.
In the setter section you want to add the following Code:
OnPropertyChanged("variable");
Your result should now look like this:
public FlowDocument FlowDoc
{
get
{
return _flowDoc;
}
set
{
_flowDoc = value;
OnPropertyChanged("FlowDoc");
}
}
Important: remember to apply this to all your variables!
4. Use MVVM pattern right
In MVVM you have a Model a View and a ViewModel.
The Model is for your data so if possible don't store data in your ViewModel instead use a data class for ex.
You may have a look at this and/or this.
As I said in the beginning I'm new to all of this but i hope it helps
you. Feel free to ask.

Related

C# wpf caliburn.Micro MahApps HamburgerMenu.ContentTemplate data binding is not working

I'm making an application using Caliburn.Micro(for easy data binding and stuff) and MahApps.Metro(for designing).
I've created a View name 'MainView' which has HamburgerMenu of MahApps.
My issue is data binding is working fine under HamburgerMenu.ContentTemplate tag
Here is my HamburgerMenu.ContentTemplate xaml.
<Page x:Class="Sample.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cal="http://www.caliburnproject.org"
xmlns:mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
xmlns:utils="clr-namespace:Omni.WindowsClient.Utils"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Omni.WindowsClient.Views"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="600">
<Page.Resources>
<DataTemplate x:Key="HamburgerMenuItem"
DataType="{x:Type mah:HamburgerMenuItem}">
<Grid Height="48">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="48" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Image Margin="12"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Source="{Binding Glyph}"
Stretch="UniformToFill" />
<TextBlock Grid.Column="1"
VerticalAlignment="Center"
FontSize="16"
Foreground="White"
Text="{Binding Label}" />
</Grid>
</DataTemplate>
</Page.Resources>
<Grid>
<mah:HamburgerMenu x:Name="HamburgerMenuControl"
SelectedIndex="0"
ItemTemplate="{StaticResource HamburgerMenuItem}"
OptionsItemTemplate="{StaticResource HamburgerMenuItem}"
IsPaneOpen="True"
DisplayMode="CompactInline"
cal:Message.Attach="[Event ItemClick] = [Action ShowDetails(HamburgerMenuControl.SelectedItem)]"
DataContext="{Binding RelativeSource={RelativeSource self}}">
<mah:HamburgerMenu.ItemsSource>
<mah:HamburgerMenuItemCollection>
<mah:HamburgerMenuItem Label="System Status">
<mah:HamburgerMenuItem.Tag>
<iconPacks:PackIconFontAwesome Width="22"
Height="22"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Kind="Tasks" />
</mah:HamburgerMenuItem.Tag>
</mah:HamburgerMenuItem>
<mah:HamburgerMenuItem Label="Inbox">
<mah:HamburgerMenuItem.Tag>
<iconPacks:PackIconFontAwesome Width="22"
Height="22"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Kind="Inbox" />
</mah:HamburgerMenuItem.Tag>
</mah:HamburgerMenuItem>
<mah:HamburgerMenuItem.Tag>
<iconPacks:PackIconFontAwesome Width="22"
Height="22"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Kind="Certificate" />
</mah:HamburgerMenuItem.Tag>
</mah:HamburgerMenuItem>
</mah:HamburgerMenuItemCollection>
</mah:HamburgerMenu.ItemsSource>
<mah:HamburgerMenu.ContentTemplate>
<DataTemplate DataType="{x:Type mah:HamburgerMenuItem}">
<Grid utils:GridUtils.RowDefinitions="48,*">
<!--cal:Action.TargetWithoutContext="{Binding ElementName=HamburgerMenuControl, Path=DataContext}"-->
<Border Grid.Row="0"
Background="{DynamicResource MahApps.Metro.HamburgerMenu.PaneBackgroundBrush}">
<TextBlock x:Name="Header"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="24"
Foreground="White" />
<!--Text="{Binding Path=Header, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"-->
</Border>
<Frame Grid.Row="1"
cal:Message.Attach="RegisterFrame($source)"
DataContext="{x:Null}"
NavigationUIVisibility="Hidden" />
</Grid>
</DataTemplate>
</mah:HamburgerMenu.ContentTemplate>
</mah:HamburgerMenu>
</Grid>
</Page>
and respective view model code is:
using Caliburn.Micro;
using MahApps.Metro.Controls;
using System.Windows.Controls;
namespace Sample.ViewModels
{
public class MainViewModel : Screen
{
private readonly SimpleContainer _container;
private INavigationService _navigationService;
private string _header;
public string HeaderTitle
{
get { return _header; }
set
{
_header = value;
NotifyOfPropertyChange();
}
}
public MainViewModel(SimpleContainer container)
{
this._container = container;
DisplayName = "Main";
}
public void RegisterFrame(Frame frame)
{
_navigationService = new FrameAdapter(frame);
_container.Instance(_navigationService);
_navigationService.NavigateToViewModel(typeof(SystemStatusViewModel));
HeaderTitle = "System Status";
}
public void ShowDetails(HamburgerMenuItem menuItem)
{
switch (menuItem.Label)
{
case "System Status":
_navigationService.NavigateToViewModel(typeof(SystemStatusViewModel));
HeaderTitle = "System Status";
break;
case "Inbox":
_navigationService.NavigateToViewModel(typeof(InboxViewModel));
HeaderTitle = "Inbox";
break;
default:
break;
}
}
}
}
I want to change View in frame under HamburgerMenu.ContentTemplate when I click on menu item.
Like System Status view is SystemStatusView
and Inbox view is InboxView.
My code is working fine (it changes the view in frame and change the Header label too) if I don't use HamburgerMenu.ContentTemplate. But I want to use HamburgerMenu.ContentTemplate to work with HamburgerMenu.
Thanks!
If it's working fine if you don't use HamburgerMenu.ContentTemplate, but stops working when you do, the problem is probably with you overwriting the default template in a way that doesn't support all functionalities of a control.
I'd suggest you to use Blend to get the default HamburgerMenu.ContentTemplate, then just edit it to your needs, without changing too much (keep in mind that names of controls used as a template may have a crucial meaning, so be careful what you are editing).
If you don't know how to use Blend to get your control's template, here is a simple tutorial described in a documentation of Telerik controls (don't worry, it works the same for all controls). You just need to create copy of a HamburgerMenu.ContentTemplate, paste it to your application and you are good to go (editing).

WPF Binding vs. Manual Update in Codebehind

So, I've got a UserControl which displays basic information about a customer extracted from a file object.
The control looks like this:
<UserControl x:Class="Ns.Gui.pnlDebtor"
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="230" d:DesignWidth="460" xmlns:my="clr-namespace:Ns.Gui">
<Grid>
<GroupBox Header="Debtor" HorizontalAlignment="Stretch" Margin="0,0,0,0" Name="groupBox1" VerticalAlignment="Stretch">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="28" />
<RowDefinition Height="56" />
<RowDefinition Height="28" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80*" />
<ColumnDefinition Width="380*" />
</Grid.ColumnDefinitions>
<Label Content="Name:" Height="28" HorizontalAlignment="Left" Name="lblName" VerticalAlignment="Top" />
<Label Content="Address:" Grid.Row="1" Height="28" HorizontalAlignment="Left" Name="lblAddress" VerticalAlignment="Top" />
<Label Content="Customer Nr.:" Grid.Row="2" Height="28" HorizontalAlignment="Left" Name="lblCustomerNr" VerticalAlignment="Top" />
<TextBox Grid.Column="1" Height="23" HorizontalAlignment="Stretch" Margin="0,0,0,0" Name="tbName" VerticalAlignment="Top" IsEnabled="False" IsReadOnly="False" Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:pnlDebtor, AncestorLevel=1}, Path=DebtorName, Mode=OneWay}" />
<TextBox Grid.Column="1" Grid.Row="1" Height="46" HorizontalAlignment="Stretch" Margin="0,0,0,0" Name="tbAddress" VerticalAlignment="Top" IsReadOnly="True" IsEnabled="False" Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:pnlDebtor, AncestorLevel=1}, Path=Adresse, Mode=OneWay}" />
<TextBox Grid.Column="1" Grid.Row="2" Height="23" HorizontalAlignment="Stretch" Margin="0,0,0,0" Name="tbCustomerNr" VerticalAlignment="Top" IsEnabled="False" IsReadOnly="True" />
<Button Content="Debtor Details" Grid.ColumnSpan="2" Grid.Row="3" Height="23" HorizontalAlignment="Left" Margin="0,0,0,0" Name="btnDetails" VerticalAlignment="Top" />
</Grid>
</GroupBox>
</Grid>
</UserControl>
My Name and Address Textboxes are bound to DebtorName and DebtorAddress properties of the pnlDebtor UserControl.
Codebehind is like so:
public partial class pnlDebtor : UserControl
{
private MyFile file = null;
public MyFile File
{
get
{
return file;
}
set
{
file = value;
tbCustomerNr.Text = file.CustomerNo;
}
}
private Contact debtor = null;
public Contact Debtor
{
get
{
if (debtor == null)
{
if (File != null)
{
debtor = AbstractDataObject.GetObject4ID<Contact>(File.DebtorID);
}
}
return debtor;
}
}
private Address debtorAddress = null;
public string Address
{
get
{
string result = string.Empty;
if (debtorAddress == null)
{
if (Debtor != null)
{
List<Address> lsAddresses = AbstractDataObject.GetObject4NonIdProperty<Address>("ContactID", Debtor.ID);
if (lsAddresses.Any())
{
debtorAddress = lsAddresses[0];
result += lsAddresses[0].Street + "\r\n"
+ lsAddresses[0].PostalCode + " " + lsAddresses[0].City;
}
}
}
else
{
result += debtorAddress.Street + "\r\n"
+ debtorAddress.PostalCode + " " + debtorAddress.City;
}
return result;
}
}
private string strDebtorName = string.Empty;
public string DebtorName
{
get
{
if (strDebtorName== string.Empty)
{
if (Debtor != null)
{
strDebtorName = Debtor.Name1;
if (Debtor.FirstName != null)
strSchuldnerName += ", " + Debtor.FirstName;
}
}
return strDebtorName;
}
}
public pnlDebtor()
{
InitializeComponent();
}
}
As you can see, unlike the Name and Address Textboxes, my CustomerNr Textbox is populated in the codebehind. When I pass in my file object, I extract the customer number and assign that value to the Text property of the appropriate Textbox. Both methods work, but the first method (Binding) seems to be prefered for WPF. Why?
To me, the disadvantages are:
1) Logic and presentation aren't separated. If I send my xaml to someone in design, there's a chance they could screw up my binding.
2) If I'm debugging and set a breakpoint, the Text property of my bound Textboxes is always an empty string. I can't see what's going on.
So, what are the advantages of using binding? Why is it the preferred method? Use small words. This is my first WPF project. :)
I think the missing link here is MVVM. Your bindings to properties in codebehind mean that that particular UserControl is still tightly coupled to that particular class. I wouldn't have said that it's any better than the version without bindings. The codebehind class won't compile unless the XAML page is bundled with it, because there's a reference to tbCustomerNr.Text.
Using MVVM and bindings, the ViewModel is completely isolated from the View. I can, if I like, delete the Views entirely from my program and the ViewModels will still compile without any complaints. This means that the ViewModel logic can be reused easily, and logic and UI development tasks can be separated cleanly.

Find the Fill value of a rectangle inside a comboboxItem

I have following xaml code:
<Window x:Class="WPF_les_3.Oefening_4"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Oefening_4" Height="300" Width="300">
<StackPanel Width="auto" Margin="20px">
<ComboBox Width="100" SelectionChanged="ComboBox_Selected" x:Name="comboBox">
<ComboBoxItem>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="Red" Height="20" Width="20"/>
<TextBlock Text=" Red"/>
</StackPanel>
</ComboBoxItem>
<ComboBoxItem>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="Yellow" Height="20" Width="20"/>
<TextBlock Text=" Yellow"/>
</StackPanel>
</ComboBoxItem>
<ComboBoxItem>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="Green" Height="20" Width="20"/>
<TextBlock Text=" Green"/>
</StackPanel>
</ComboBoxItem>
</ComboBox>
</StackPanel>
As you see, inside my ComboboxItems I have a rectangle and a textblock. Now I want to retreive the fill color of the rectangle (or the text of the textblock, it's the same) when my selectionchanged event is handled, so I can change the background of the window according to the selected color (which is the goal of the excercise).
To elaborate on my comment above, this is the Correct way to achieve what you need in WPF:
First of all, create a proper ViewModel that contains the list of available colors and a SelectedColor property:
public class ColorsViewModel
{
public ObservableCollection<string> Colors { get; private set; }
private string _selectedColor;
public string SelectedColor
{
get { return _selectedColor; }
set
{
_selectedColor = value;
MessageBox.Show("Selected Color: " + value); //message box here to show the code is actually working.
}
}
//... More code here in a moment
}
Then, make sure you populate the color collection with relevant data. In the case of colors specifically, WPF has built-in TypeConverters that can convert from (for example) string to System.Windows.Media.Color implicitly, therefore we can leverage that to simplify our code and use simple strings:
//Continuation of the above code
public ColorsViewModel()
{
Colors = new ObservableCollection<string>
{
"Red",
"Green",
"Blue",
"Yellow",
};
}
And finally create the UI in XAML using proper DataBinding:
<Window x:Class="WpfApplication3.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">
<ComboBox ItemsSource="{Binding Colors}"
SelectedItem="{Binding SelectedColor}"
VerticalAlignment="Center" HorizontalAlignment="Center">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="{Binding}" Height="20" Width="20"/>
<TextBlock Text="{Binding}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Window>
Result:
The change event is fired and the ComboBox.SelectedItem has the info you need.
You have to analyze the SelectedItem like my following method:
private void comboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBoxItem comboBoxItem = this.comboBox.SelectedItem as ComboBoxItem;
if (comboBoxItem != null)
{
StackPanel stackPanel = comboBoxItem.Content as StackPanel;
if(stackPanel != null && stackPanel.Children[0] is Rectangle)
{
var fill = (stackPanel.Children[0] as Rectangle).Fill;
}
}
}
Here you get the fill of the rectangle and can handle this or do your stuff.
But be patient, this code is created exactly for you sample (ComboBoxItem with Content StackPanel with Children[0] as Rectangle). Changes will iterrupt the process ;)

Windows Phone - SyndicationFeed issue

I need to do application for Windows Phone for school project. I've followed tutorial to do RSS reader, but it doesn't work and I don't know why.
I'm getting following error (after it runs):
System.Windows.Data Error: BindingExpression path error: 'Items'
property not found on
'Expression.Blend.SampleData.JustTestingData.JustTestingData'
'Expression.Blend.SampleData.JustTestingData.JustTestingData'
(HashCode=12963143). BindingExpression: Path='Items'
DataItem='Expression.Blend.SampleData.JustTestingData.JustTestingData'
(HashCode=12963143); target element is
'System.Windows.Controls.ListBox' (Name='FeedContent'); target
property is 'ItemsSource' (type 'System.Collections.IEnumerable')..
Here is my .cs file:
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainPage_Loaded);
}
public void MainPage_Loaded(object sender, RoutedEventArgs e)
{
WebClient wc = new WebClient();
wc.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
wc.OpenWriteAsync(new Uri("http://www.twojapogoda.pl/wiadomosci.xml"));
}
public void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
SyndicationFeed feed;
using (XmlReader reader = XmlReader.Create(e.Result))
{
feed = SyndicationFeed.Load(reader);
FeedContent.ItemsSource = feed.Items;
}
}
}
Here is my xaml:
<phone:PhoneApplicationPage
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Syndication="clr-namespace:System.ServiceModel.Syndication;assembly=System.ServiceModel.Syndication"
x:Class="JustTestIt.MainPage"
mc:Ignorable="d"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">
<phone:PhoneApplicationPage.Resources>
<DataTemplate x:Key="ItemTemplate">
<StackPanel>
<CheckBox IsChecked="{Binding date, Mode=TwoWay}"/>
<TextBlock Text="{Binding title}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="ItemTemplate1">
<StackPanel>
<CheckBox IsChecked="{Binding date, Mode=TwoWay}"/>
<TextBlock Text="{Binding title}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="ItemTemplate2">
<StackPanel Width="381">
<TextBlock Text="{Binding title}" FontSize="32" Foreground="#FFFF8B00" Margin="0,0,10,0" FontFamily="Segoe WP Semibold"/>
<TextBlock Text="{Binding date}" Foreground="White" FontFamily="Segoe WP Light"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="SyndicationItemTemplate">
<StackPanel>
<TextBlock Text="{Binding Title.Text}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="SyndicationItemTemplate1">
<StackPanel>
<TextBlock Text="{Binding Title.Text}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="SyndicationItemTemplate2">
<StackPanel>
<TextBlock Text="{Binding Title.Text}"/>
</StackPanel>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>
<phone:PhoneApplicationPage.FontFamily>
<StaticResource ResourceKey="PhoneFontFamilyNormal"/>
</phone:PhoneApplicationPage.FontFamily>
<phone:PhoneApplicationPage.FontSize>
<StaticResource ResourceKey="PhoneFontSizeNormal"/>
</phone:PhoneApplicationPage.FontSize>
<phone:PhoneApplicationPage.Foreground>
<StaticResource ResourceKey="PhoneForegroundBrush"/>
</phone:PhoneApplicationPage.Foreground>
<Grid x:Name="LayoutRoot" Background="Transparent" DataContext="{Binding Source={StaticResource JustTestingData}}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<phone:Panorama Foreground="White" FontFamily="Segoe WP Light" Background="Black">
<phone:Panorama.Title>
<TextBlock Text="JustTest it!"/>
</phone:Panorama.Title>
<phone:PanoramaItem x:Name="headers" CacheMode="{x:Null}" Header="">
<phone:PanoramaItem.RenderTransform>
<TranslateTransform/>
</phone:PanoramaItem.RenderTransform>
<Grid Margin="0">
<ListBox HorizontalAlignment="Left" ItemTemplate="{StaticResource ItemTemplate2}" ItemsSource="{Binding Collection}" VerticalAlignment="Top" Width="410"/>
</Grid>
</phone:PanoramaItem>
<phone:PanoramaItem x:Name="articles" CacheMode="{x:Null}" Header="" d:DataContext="{d:DesignData /SampleData/SyndicationFeedSampleData.xaml}">
<phone:PanoramaItem.RenderTransform>
<TranslateTransform/>
</phone:PanoramaItem.RenderTransform>
<Grid>
<ListBox x:Name="FeedContent" HorizontalAlignment="Left" ItemTemplate="{StaticResource SyndicationItemTemplate2}" ItemsSource="{Binding Items}" VerticalAlignment="Top"/>
</Grid>
</phone:PanoramaItem>
</phone:Panorama>
</Grid>
</phone:PhoneApplicationPage>
What am I doing wrong, that nothing loads from source?
I'm using blend and visual studio 2013.
Your error is on this line
<ListBox x:Name="FeedContent" HorizontalAlignment="Left" ItemTemplate="{StaticResource
SyndicationItemTemplate2}" ItemsSource="{Binding Items}" VerticalAlignment="Top"/>
The error is right here
ItemsSource="{Binding Items}"
Its saying it cant see a property named "Items"
This could be for a number of reasons. I need to see all of your CS file to give you a more specific answer
If this IS all of your cs file then the there is a HUGE problem. You have no properties....
You could start by adding this.
public ObservableCollection Items {get;set;}
Also it doesn't appear that you are setting the datacontext.
Above InitializeComponent do this.
this.DataContext = this;
Edit
Based on further evaluation of your CS file and some creative extrapolation it looks like you need to do the following.
Make your SyndicationFeed object a property
private SyndicationFeed _feed;
public SyndicationFeed feed {get{return _feed;} set{_feed = value; OnPropertyChanged("feed");}
Set your datacontext to
this.Datacontext = feed;
implement
INotifyPropertyChanged
Add a property Changed event handler
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
I am pretty sure your SyndicationFeed class needs to implement INotifyPropertyChanged also.
Either of the above two solutions should work.
The problem with the error concerned with missing property (in the code you have provided) explained AMR.
Also you should be aware that the line in the code:
FeedContent.ItemsSource = feed.Items;
would destroy the Binding you have defined in xaml.
The problem why your FeedContent isn't filled is concerned with lines:
wc.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
wc.OpenWriteAsync(new Uri("http://www.twojapogoda.pl/wiadomosci.xml"));
You are subscribing to the event OpenReadCompleted, but running OpenWriteAsync() method. Your event won't get fired at all (and you probably also won't be able to write to this Uri). Change it to:
wc.OpenReadAsync(new Uri("http://www.twojapogoda.pl/wiadomosci.xml"));
then your event will be fired and SyndicationFeed loaded.

How to add a table/grid to a vb.net/C# WPF window?

I have an asynchronous server listening for clients on a local network. As each client sends a connect message to the server, I want the server to display the client's name in a table.
Assume that I already have the client's name and IP address as a string ClientDetails separated by a _ e.g. "PC5_192.168.1.10"
*EDIT *
What I want
As clients join in, I would like to add each client as a new row to the table/grid.
I am using WPF. Either vb.net or C# answer will be fine, I can translate it myself.
I Prepared a small example of "the WPF way" to do this.
It looks like this in my computer:
Im using random values as the data source:
public class RandomConnectionAdder
{
public Timer timer;
public Random random = new Random();
public Action<Connection> OnConnectionAdded { get; set; }
public RandomConnectionAdder(Action<Connection> onConnectionAdded)
{
OnConnectionAdded = onConnectionAdded;
timer = new Timer(x => AddConnection(), null, 5000, 2000);
}
private void AddConnection()
{
var computernumber = random.Next(1, 50);
var newrandomconnection = new Connection()
{
ComputerName = "PC" + computernumber.ToString(),
IPAddress = "192.168.1." + computernumber,
ConnectionTime = DateTime.Now
};
if (OnConnectionAdded != null)
OnConnectionAdded(newrandomconnection);
}
}
Notice that I added a level of indirection via the use of the Action<Connection> delegate to keep the separation of concerns. The "listener" has the responsibility of listening for incoming connections, what to do when a new connection is added is outside its scope.
This is the Model class:
public class Connection: INotifyPropertyChanged
{
private string _computerName;
public string ComputerName
{
get { return _computerName; }
set
{
_computerName = value;
OnPropertyChanged("ComputerName");
}
}
private string _ipAddress;
public string IPAddress
{
get { return _ipAddress; }
set
{
_ipAddress = value;
OnPropertyChanged("IPAddress");
}
}
private DateTime _connectionTime;
public DateTime ConnectionTime
{
get { return _connectionTime; }
set
{
_connectionTime = value;
OnPropertyChanged("ConnectionTime");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
This is the Window Code-behind:
public partial class Window6 : Window
{
private RandomConnectionAdder adder;
private ObservableCollection<Connection> Connections;
public Window6()
{
InitializeComponent();
Connections = new ObservableCollection<Connection>();
adder = new RandomConnectionAdder(x => Dispatcher.BeginInvoke((Action) (() => AddConnection(x))));
DataContext = Connections;
}
private void AddConnection(Connection connection)
{
Connections.Add(connection);
}
}
As you can see, the window instantiates the RandomConnectionAdder and sets its OnConnectionAdded action to a lambda that dispatches the addition of the item to the ObservableCollection to the UI Thread via the Dispatcher.
Finally, this is the whole XAML:
<Window x:Class="WpfApplication5.Window6"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window6" Height="300" Width="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<GroupBox Header="DataGrid">
<DataGrid ItemsSource="{Binding}" AutoGenerateColumns="False" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Computer Name" Binding="{Binding ComputerName}"/>
<DataGridTextColumn Header="IP Address" Binding="{Binding IPAddress}"/>
<DataGridTextColumn Header="Connection Time" Binding="{Binding ConnectionTime, StringFormat='HH:mm:ss'}"/>
</DataGrid.Columns>
</DataGrid>
</GroupBox>
<GroupBox Header="Large Icons (ListBox)" Grid.Column="1">
<ListBox ItemsSource="{Binding}">
<ListBox.Template>
<ControlTemplate>
<ItemsPresenter/>
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel Margin="5" Width="120">
<StackPanel DockPanel.Dock="Bottom">
<TextBlock Text="{Binding ComputerName}" TextAlignment="Center"/>
<TextBlock Text="{Binding IPAddress}" TextAlignment="Center"/>
<TextBlock Text="{Binding ConnectionTime, StringFormat='HH:mm:ss'}" TextAlignment="Center"/>
</StackPanel>
<Border Height="60" Width="60" BorderBrush="Black" BorderThickness="1">
<TextBlock Text="Some Icon" VerticalAlignment="Center" TextAlignment="Center"/>
</Border>
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</GroupBox>
<GroupBox Header="Tiles (ListBox)" Grid.Column="2">
<ListBox ItemsSource="{Binding}">
<ListBox.Template>
<ControlTemplate>
<ItemsPresenter/>
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel Margin="5" Width="120">
<Border Height="40" Width="50" BorderBrush="Black" BorderThickness="1" DockPanel.Dock="Left">
<TextBlock Text="Some Icon" VerticalAlignment="Center" TextAlignment="Center"/>
</Border>
<StackPanel>
<TextBlock Text="{Binding ComputerName}" TextAlignment="Center"/>
<TextBlock Text="{Binding IPAddress}" TextAlignment="Center"/>
<TextBlock Text="{Binding ConnectionTime, StringFormat='HH:mm:ss'}" TextAlignment="Center"/>
</StackPanel>
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</GroupBox>
</Grid>
</Window>
As you can see, Im in no way manipulating UI elements in code. This keeps the code clean and easy and well separated, as the Application logic / Data is in no way dependant on the state of UI elements.
Also, in this example can be seen the concept of "Binding several different Views to the Same ViewModel", which in this case is the ObservableCollection itself.
This is "the WPF" approach for everything. You almost never have to manipulate UI elements in code, at least not in regards to application logic or data.
Just copy and paste my code in a File -> New Project -> WPF Application and see the results for yourself.

Categories