I'm developing a kind of Store or something, where I read an RSS Feed and display its content in a ListBox. The RSS Feed contains additional data (for example "download" or "customCategory") that I use (or want to use) to sort the results of the ListBox. The ListBox looks like this:
<ListBox Grid.Column="2" HorizontalAlignment="Stretch" Name="ItemsListParent" VerticalAlignment="Stretch" Margin="0,25,0,0">
<ItemsControl Name="ItemsList" ItemsSource="{Binding Source={StaticResource rssData}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Name="itemElement" Orientation="Horizontal" Loaded="itemElement_Loaded">
<StackPanel.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="White" Offset="0.15" />
<GradientStop Color="LightGray" Offset="0.85" />
<GradientStop Color="White" Offset="1" />
</LinearGradientBrush>
</StackPanel.Background>
<!--<Image Width="15" Margin="2" Source="{Binding XPath=url, Path=InnerText}"/>-->
<!--<TextBlock Margin="2" FontSize="16" VerticalAlignment="Center" Text="{Binding XPath=title}" FontWeight="Normal">
<Hyperlink Name="lnkGoToArticle" Tag="{Binding XPath=link, Path=InnerText}" Click="lnkGoToArticle_Click">
>>
</Hyperlink>
<Button Name="lnkDownload" Tag="{Binding XPath=download, Path=InnerText}" Style="{DynamicResource NoChromeButton}" Click="lnkDownload_Click">
<Image Source="Images/download31.png" Name="DownloadIcon" Width="30" Height="30" />
</Button>
</TextBlock>-->
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ListBox>
the code in
<!-- -->
is what I rewrote to C#, as I thought I can figure out how to sort the XML. The point is, that I've created
void UpdateListBox()
{
ItemsList.Items.Clear();
selected = (string)CategoriesList.SelectedItem;
//if (selected==xmldp
/*System.Xml.XmlDocument data = new System.Xml.XmlDocument();
data.Load(#"http://www.andystore.bluefile.cz/?feed=rss2");
xmldp.Document = data;
xmldp.XPath = "//item";*/
Thickness mrg = new Thickness();
mrg.Left = 2;
mrg.Right = 2;
mrg.Top = 2;
mrg.Bottom = 2;
TextBlock itemTitle=new TextBlock();
itemTitle.Margin=mrg;
itemTitle.FontSize=16;
itemTitle.VerticalAlignment = VerticalAlignment.Center;
itemTitle.Text = "{Binding XPath=title}";
itemTitle.FontWeight = FontWeights.Normal;
itemTitle.Name="itemTitle";
Binding itemTitleBinding=new Binding();
itemTitleBinding.XPath="title";
itemTitle.SetBinding(TextBlock.TextProperty,itemTitleBinding);
itemElement.Children.Add(itemTitle);
itemElement.RegisterName(itemTitle.Name, itemTitle);
Label gta = new Label();
Hyperlink goToArticle = new Hyperlink();
goToArticle.Click += new RoutedEventHandler(lnkGoToArticle_Click);
goToArticle.Inlines.Add(#">>");
Binding goToArticleBinding = new Binding();
goToArticleBinding.Path = new PropertyPath("InnerText");
goToArticleBinding.XPath = "link";
goToArticle.SetBinding(Hyperlink.TagProperty, goToArticleBinding);
gta.Content = goToArticle;
itemElement.Children.Add(gta);
itemElement.RegisterName(goToArticle.Name, goToArticle);
Button downloadButton = new Button();
downloadButton.Name = "lnkDownload";
downloadButton.Cursor = Cursors.Hand;
downloadButton.Click += new RoutedEventHandler(lnkDownload_Click);
Binding downloadButtonBinding = new Binding();
downloadButtonBinding.XPath = "download";
downloadButtonBinding.Path = new PropertyPath("InnerText");
downloadButton.SetBinding(Button.TagProperty, downloadButtonBinding);
Style downloadButtonStyle = this.FindResource("NoChromeButton") as Style;
downloadButton.Style = downloadButtonStyle;
BitmapImage dbiBitmap = new BitmapImage();
dbiBitmap.BeginInit();
dbiBitmap.UriSource = new Uri("pack://application:,,,/AndyLaunchWPF;component/Images/download31.png");
dbiBitmap.EndInit();
Image dbi = new Image();
dbi.Width = 30;
dbi.Height = 30;
dbi.Name = "downloadIcon";
dbi.Source = dbiBitmap;
downloadButton.Content = dbi;
itemElement.Children.Add(downloadButton);
itemElement.RegisterName(downloadButton.Name, downloadButton);
//itemElement.Children.Add(dbi);
//itemElement.RegisterName(dbi.Name, dbi);
}
but it completes the whole listbox (as before in wpf code) without repeating the calling!! I wanted to add somekind of condition for sorting such as if(xmldp.IDontKnowTheExactName==selectedCategory){display the textblock} else {do not display it and go to next item in XML} but i really dont know how to do it. Please be patient with me, as i am new to WPF and this is also my first question. In the case that you didn't really got what I'm trying to achieve, here is a simple list:
1) I want to load the XML and display all it's items in ItemsList
2) I want to select an item in the ListBox called categoriesList and based on the selection update ItemsList to display only items that have their customCategory==selected (selected is a string that will be updated depending on the categoriesList selection)
Problem is, I dont know where to put the condition, nor how it should look like and if it's even possible.
Hope you understood and you are able to help me.
Thanks for any answer ;) Andy
OK forget constructing the view via code, that's not how WPF is supposed to work. Go back to your original xaml template.
Now the problem you have is that you want to sort and filter the items in the ItemsControl. To do this you need to bind the ItemsSource of the ItemsControl to a CollectionView based on the rssFeed, instead of binding to the rssFeed itself.
CollectionView allows you to easily sort and filter collections.
By the way, you appear to have a redundant ListBox in your XAML. It's not doing anything since you are declaring an ItemsControl inside it. ListBox already derives from ItemsControl.
If you want a scrollbar then just use the ListBox.
Related
I don't know How to name this, so I don't know what to google for, so I decided to ask here.
My XAML Code is:
<Expander ExpandDirection="Right">
<Expander.Header>
<TextBlock Text="PC and Notebook" RenderTransformOrigin=".5,.5">
<TextBlock.LayoutTransform>
<RotateTransform Angle="90" />
</TextBlock.LayoutTransform>
</TextBlock>
</Expander.Header>
<TreeView>
</TreeView>
</Expander>
So I want to make this XAML Code into Code-Behind (for a while loop).
I started simple with:
Expander cat_expander = new Expander();
cat_expander.ExpandDirection = ExpandDirection.Right;
And my problem now is. How to add <Expander.Header> dynamically ? How people name this thing when a Control has another control inside it?
I hope u can understand what I mean.
You set the Header property to a TextBlock:
Expander cat_expander = new Expander();
cat_expander.ExpandDirection = ExpandDirection.Right;
TextBlock textBlock = new TextBlock();
textBlock.Text = "PC and Notebook";
textBlock.RenderTransformOrigin = new Point(0.5, 0.5);
textBlock.LayoutTransform = new RotateTransform() { Angle = 90 };
cat_expander.Header = textBlock;
cat_expander.Content = new TreeView();
As the title says I have based my work on this
And that works! The problem is that I can't get to solve those problems:
Description column not wide enough (can't get to set it to the whole space with the star keyword
Items large enough to fit only one on line
Header colour not white
The listviewCode is:
<ListView x:Name="lvPPtab1" Grid.Row="2" FontSize="12" Background="{x:Null}" BorderBrush="Gainsboro" BorderThickness="5" Margin="10,12.2,10,8.4" VerticalAlignment="Stretch" PreviewMouseLeftButtonDown="ListBox_PreviewMouseLeftButtonDown" SelectionChanged="ListView_SelectionChanged">
<ListView.Effect>
<DropShadowEffect ShadowDepth="4" Direction="330" Color="Black" Opacity="0.5" BlurRadius="4"/>
</ListView.Effect>
<ListView.ItemsPanel >
<ItemsPanelTemplate >
<WrapPanel Orientation="Horizontal" Width="250" Background="{x:Null}" VerticalAlignment="Top"></WrapPanel>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
and it can be both programmatically filled with items (real listview like) or with grids (datagrid like). In this case I am focusing on the datagrid like case.
ObservableCollection<String> obc = new ObservableCollection<string>();
obc.Add("AAAA");
obc.Add("BBB");
GridView myGridView = new GridView();
myGridView.AllowsColumnReorder = true;
GridViewColumn gvc = new GridViewColumn();
//gvc1.DisplayMemberBinding = new Binding("ColumnOneHeader");
gvc.Header = Languages.Word(eWords.Description);
myGridView.Columns.Add(gvc);
lvPPtab1.View = myGridView;
lvPPtab1.Items.Add("AAAA");
lvPPtab1.Items.Add("BBB");
lvPPtab1.Background = Brushes.Transparent;
lvPPtab1.Foreground = Brushes.Gainsboro;
What I have to simulate is something like what happens in explorer when you display images or names
I'm experimenting with code-behind created WPF masks as prototype for a WPF mask designer.
In my ViewModel i have a DataTable and a DataView (which is simply the DefaultView of the DataTable).
In my DataTable i got two columns ("vorname" and "nachname") and four rows.
In my WPF mask i want to have a DataGrid and two TextBoxes, which are binded to the SelectedItem of the DataGrid and the columns (either "vorname" or "nachname").
When i select an item in the DatGrid at runtime, the data from that item shall be showed in the TextBoxes.
First i tried to define the DataGrid in the XAML file and generate the TextBoxes an their bindings in code.
Here it works fine.
I select an item in the DataGrid and the data of the item is showed in the TextBoxes.
But when i generate the grid in code, it doesn't work anymore.
Is there some sort of NotifyOnSelectedIndexChanged, that i'm missing?
Any help will be appreciated.
This is the XAML:
<Window x:Class="DesignerTest.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TestWindow"
Height="400"
Width="600">
<DockPanel x:Name="mainpanel">
<!--<DataGrid x:Name="datagrid"
DockPanel.Dock="Top"
Height="120" />-->
<WrapPanel x:Name="wrappanel">
<!--<TextBox x:Name="vornameSelected" Width="150" Margin="5" Text="{Binding SelectedItem.vorname, ElementName=datagrid}" IsEnabled="False" />
<TextBox x:Name="nachnameSelected" Width="150" Margin="5" Text="{Binding SelectedItem.nachname, ElementName=datagrid}" IsEnabled="False" />-->
<!--<TextBox x:Name="vornameSelected" Width="150" Margin="5" IsEnabled="False" />
<TextBox x:Name="nachnameSelected" Width="150" Margin="5" IsEnabled="False" />-->
</WrapPanel>
</DockPanel>
</Window>
And this is the code for creating and binding:
// The ViewModel und the DataTable are created.
_vm = new SerializingTestViewModel();
_vm.CreateDataTable();
this.DataContext = _vm.DataTable;
// The DataGrid and it's Binding are created.
DataGrid datagrid = new DataGrid();
datagrid.Name = "datagrid";
DockPanel.SetDock(datagrid, Dock.Top);
datagrid.Height = 120;
datagrid.ItemsSource = _vm.DataSource;
mainpanel.Children.Add(datagrid);
// The Textboxes and the Bindings are created.
TextBox vornameSelected = new TextBox();
vornameSelected.Name = "vornameSelected";
vornameSelected.Width = 150;
Thickness margin = new Thickness(5);
vornameSelected.SetValue(TextBox.MarginProperty, margin);
vornameSelected.IsEnabled = false;
Binding selectedItemBinding = new Binding();
selectedItemBinding.ElementName = "datagrid";
selectedItemBinding.Path = new PropertyPath("SelectedItem.vorname");
vornameSelected.SetBinding(TextBox.TextProperty, selectedItemBinding);
wrappanel.Children.Add(vornameSelected);
TextBox nachnameSelected = new TextBox();
nachnameSelected.Name = "nachnameSelected";
nachnameSelected.Width = 150;
margin = new Thickness(5);
nachnameSelected.SetValue(TextBox.MarginProperty, margin);
nachnameSelected.IsEnabled = false;
selectedItemBinding = new Binding();
selectedItemBinding.ElementName = "datagrid";
selectedItemBinding.Path = new PropertyPath("SelectedItem.nachname");
nachnameSelected.SetBinding(TextBox.TextProperty, selectedItemBinding);
wrappanel.Children.Add(nachnameSelected);
Try setting your binding source using the Source property instead of ElementName
//selectedItemBinding.ElementName = "datagrid"
selectedItemBinding.Source = datagrid;
The problem might be that the ElementName lookup for items is not working as expected because items are added dynamically at runtime via code behind.
I have the follow XAML:
<ContentControl HorizontalAlignment="Left" HorizontalContentAlignment="Left" Content="{Binding TotalReviewWordBlock}" Width="465" Margin="5,10,0,5" Foreground="#FF2D2D2D" Background="White"/>
and its binded to the following property:-
public StackPanel TotalReviewWordBlock
{
get
{
StackPanel st = new StackPanel();
st.Orientation = Orientation.Horizontal;
st.Background = new SolidColorBrush(Colors.White);
Paragraph pgf = new Paragraph();
Run r = new Run();
r.Text = App.Convert("Blah ");
r.FontWeight = FontWeights.Bold;
r.Foreground = new SolidColorBrush(CommonLib.rgbFromHexString("#FF2D2D2D"));
pgf.Inlines.Add(r);
int Rating = (int)(this.userrating * 2);
string ratingReplacement;
(some more code in the property itself...)
Run run = new Run();
run.Text = " " + this.myText;
run.Foreground = new SolidColorBrush(CommonLib.rgbFromHexString("#FF2D2D2D"));
pgf.Inlines.Add(run);
RichTextBox rtb = new RichTextBox();
rtb.TextWrapping = TextWrapping.Wrap;
rtb.Width = 450;
rtb.Blocks.Add(pgf);
st.Children.Add(rtb);
st.Background = new SolidColorBrush(Colors.White);
return st;
}
}
The problem is when the text is too much(say more that a 1000 character), or the height of the stackpanel is a lot, Its background becomes black. Its as if the stackpanel breaks) I noticed this earlier but at that time it was in a listbox and had multiple items to i simply made the width of each item 480, used blank grids instead of margins and it was "covered". But this time its just one big chunk of text(in a Paragraph). Let me know if you need ay other info. Please help!!
I worked around a similar "black stackpanel" problem by splitting the text into paragraphs to form a List<String>. And then that list of strings would be the ItemsSource of a ListBox.
So instead of a very large StackPanel, I ended up with a long ListBox.
I also prevented user interaction in the ListBox and vertical scroll by using IsHitTestVisible="False" and ScrollViewer.VerticalScrollBarVisibility="Disabled"
So, the ListBoxended up as follows:
<ListBox x:Name="listBox" IsHitTestVisible="False" ScrollViewer.VerticalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Background="White">
<TextBlock TextWrapping="Wrap" Text="{Binding}"/>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And in code behind:
textSplitInParagraphs = new List<String>();
// add paragraphs to the list...
listBox.ItemsSource = textSplitInParagraphs;
Don't know if it is the correct workaround, but I helped me, after some time of banging my head against the table.
Hope this helps.
I am developing a silverlight navigation application and got stuck on the following problem...
The guy I am developing the app wants to have a News page where you can see all published news on the left side and the clicked (or latest news if none is clicked) on the right side. He wanted to have a header, text and publishing date for each news in the news list. Also he wanted to have paging so that there won't be to many news in the list at once...
I did this:
foreach (Model.News news in s)
{
StackPanel stackPanel = new StackPanel();
HyperlinkButton hyperlinkButton = new HyperlinkButton();
hyperlinkButton.Tag = news.Header;
hyperlinkButton.Content = news.Header;
hyperlinkButton.FontSize = 15;
hyperlinkButton.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
hyperlinkButton.Click += new RoutedEventHandler(Button_Click);
stackPanel.Children.Add(hyperlinkButton);
TextBlock textBlock = new TextBlock();
textBlock.Foreground = new SolidColorBrush(Colors.Gray);
textBlock.FontSize = 12;
textBlock.FontFamily = new FontFamily("Verdana");
textBlock.TextWrapping = TextWrapping.Wrap;
textBlock.Text = news.Text;
stackPanel.Children.Add(textBlock);
TextBlock dateTextBlock = new TextBlock();
dateTextBlock.Foreground = new SolidColorBrush(Colors.Gray);
dateTextBlock.FontSize = 10;
dateTextBlock.FontFamily = new FontFamily("Verdana");
dateTextBlock.TextWrapping = TextWrapping.Wrap;
dateTextBlock.FontWeight = FontWeights.Bold;
dateTextBlock.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
dateTextBlock.Text = news.Date.ToShortDateString();
stackPanel.Children.Add(dateTextBlock);
stackPanel.Children.Add(new TextBlock());
newsStackPanel.Children.Add(stackPanel);
}
PagedCollectionView itemListView = new PagedCollectionView(newsStackPanel.Children);
newsPager.Source = itemListView;
and all of it goes here
<Grid x:Name="LayoutRoot" Loaded="LayoutRoot_Loaded" MaxWidth="1100">
<Grid.RenderTransform>
<CompositeTransform/>
</Grid.RenderTransform>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="2"/>
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
<RichTextBox Name="contentRTB" MaxWidth="1000" Margin="10, 30, 10, 30" Grid.Column="2"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
TextWrapping="Wrap"
Style="{StaticResource RichTextBoxStyle}" IsReadOnly="True"/>
<Rectangle Grid.Column="1" Margin="0,10"
Fill="#FF0067C6"/>
<TextBlock Name="header" Foreground="#FF0067C6" FontSize="18" FontFamily="Verdana" HorizontalAlignment="Center" VerticalAlignment="Top" Grid.Column="0"></TextBlock>
<sdk:DataPager x:Name="newsPager"
DisplayMode="FirstLastNumeric"
Background="#FF0067C6"
PageSize="3"
AutoEllipsis="True"
NumericButtonCount="3"/>
<StackPanel Name="newsStackPanel" Grid.Column="0" Orientation="Vertical" Margin="0,50,0,0"/>
</Grid>
The newsPager displayes (correctly) 2 pages because i have currently 5 news and the pageSize is set to 3 but they are all displayed on the same page so I dont get the desired paging... how can i fix it
Your code is adding all the items to a StackPanel, then it is putting that StackPanel inside another StackPanel called "newsStackPanel" below the DataPager. So, right now, the DataPager has nothing to do with the display of your news articles, and you won't be seeing any paging happening.
Instead, take a look at the DataPager sample code here:
http://msdn.microsoft.com/en-us/library/system.windows.controls.datapager(VS.95).aspx#Y9406
You'll need to modify that sample code to contain a list of StackPanels like this:
List<StackPanel> itemList = new List<StackPanel>();
Then, for each of your news items, add them to that list instead of an outer StackPanel:
itemList.Add(stackPanel);
You'll then wrap that up and bind it to both your data pager and a new list control:
// Wrap the itemList in a PagedCollectionView for paging functionality
PagedCollectionView itemListView = new PagedCollectionView(itemList);
// Set the DataPager and ListBox to the same data source.
newsPager.Source = itemListView;
listBox1.ItemsSource = itemListView;
The sample uses a ListBox called "listBox1". You have lots of choices there. Perhaps replace the "newsStackPanel" with a ListBox called "newsList".
OK, that should be enough to get you through this.
Now for a little more homework:
You should really consider switching this to the MVVM pattern where you bind these values and template them instead of making UI controls in C#. This results in much cleaner code, enables much easier reuse, improves testability, and so on. There are a million zillion articles on the web for this. Here is one from MS:
http://msdn.microsoft.com/en-us/library/gg430869(v=PandP.40).aspx
I don't know if the DataPager control you're using handles the paging completely.
You could only add the news items that are on the page you want to view to the stack panel.
One easy way to do that could be to use LINQ in your for each, something like:
foreach (Model.News news in s.Skip(newsPager.PageSize * newsPager.PageIndex).Take(newsPager.PageSize))
you'd have to reinitialize the items in the pager when the page index changes then too.