wp8 public string is not wrapping in programatically created Text Block - c#

Hi Guys I'm working on Windows Phone 8 application. Here I got problem in wrapping text Block text.
When my string array was not public then wrapping is working fine, while when I created my string array public then wrapping does not work...!
I am not able to find my error.
My code is here
int a =1;
ScrollViewer scroll = new ScrollViewer();
string[] questions = new string[]
{ "Question :\n What is OOPS? \n\n Answer: \n Object-oriented programming (OOP) is a programming paradigm based on the concept of objects which are data structures that contain data in the form of fields often known as attributes and code in the form of procedures often known as methods. There are a few principle concepts that form the foundation of object-oriented programming: 1- Object \n 2- Class \n 3- Abstraction \n 4- Polymorphism \n 5- Inheritance \n 6- Encapsulation \n 7- Overloading & OverRiding "
};
int i;
private void showContent()
{
Grid ContentPanel = new Grid();
ContentPanel.Height = 400;
ContentPanel.Width = 440;
ContentPanel.Margin = new Thickness(0, 20, 0, 0);
scroll.Height = 400;
scroll.Width = 440;
scroll.VerticalScrollBarVisibility = ScrollBarVisibility.Visible;
scroll.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
TextBlock text_question = new TextBlock();
text_question.Height = Double.NaN;
text_question.Width = 420;
// text_question.Text = "Arsal Are you going.? If user is not login then there at detail page user want to add product in his wish list then there is popup comes .... Please Login There should be align text. when user goes to detail screen then first show batch 42 % off after that shows 0% off. more share button and like button alignment also changes slowly. user can identify easily.";
text_question.TextWrapping = TextWrapping.Wrap;
text_question.Margin = new Thickness(10, 10, 10, 10);
scroll.Content = questions[0];
ContentPanel.Children.Add(scroll);
//scroll.Content = questions[i];
TitlePanel.Children.Add(ContentPanel);
}
text_question.Text = ""; which is commented in this function is wrapping while the public string doesn't wrap.
I want to use string out side any function then string have to public.
private void next_click(object sender, RoutedEventArgs e)
{
// MessageBox.Show("Here is Next Question");
a = a + 1;
count.Text = a.ToString();
if (a > 1)
{
previous.IsEnabled = true;
}
if (a == 5)
{
next.IsEnabled = false;
}
if (i >= 0 && i < questions.Length)
{
i = i + 1;
scroll.Content = questions[i];
}
}

Looking at the code you posted here, the issue appears to be that you are never adding the TextBlock with the wrapping property set to the ScrollViewer. I would update this line:
scroll.Content = questions[0];
to:
scroll.Content = text_question;
and then manipulate text_question's content in the click event handlers.
That being said, I think you're making this far more complicated than it needs to be and making your UI code more complicated than necessary. I honestly can't think of a time that I've ever had a need to create UI controls in code and add them to the Children collection. Generally speaking, Windows Phone uses the MVVM pattern and your UI layout should be done via binding.
In this case, I'd do something like the following:
public class QuestionModel : INotifyPropertyChanged
{
private string[] _questions = new string[]
{
"Question :\n What is OOPS? \n\n Answer: \n Object-oriented programming (OOP) is a programming paradigm based on the concept of objects which are data structures that contain data in the form of fields often known as attributes and code in the form of procedures often known as methods. There are a few principle concepts that form the foundation of object-oriented programming: 1- Object \n 2- Class \n 3- Abstraction \n 4- Polymorphism \n 5- Inheritance \n 6- Encapsulation \n 7- Overloading & OverRiding ",
"Question 2",
"Question 3",
"Question 4"
};
private int _selectedIndex = 0;
public QuestionModel() {
PrevCommand = new DelegateCommand(() => {
if(_selectedIndex > 0) {
_selectedIndex--;
selectedIndexChanged();
}
});
NextCommand = new DelegateCommand(() => {
if(_selectedIndex < _questions.Length - 1) {
_selectedIndex++;
selectedIndexChanged();
}
});
}
private void selectedIndexChanged() {
NotifyPropertyChanged("CurrentQuestion");
NotifyPropertyChanged("QuestionText");
NotifyPropertyChanged("IsNextEnabled");
NotifyPropertyChanged("IsPrevEnabled");
}
public int CurrentQuestion
{
get { return _selectedIndex + 1; }
}
public string QuestionText
{
get { return _questions[_selectedIndex]; }
}
public bool IsNextEnabled
{
get { return _selectedIndex < _questions.Length - 1; }
}
public bool IsPreviousEnabled
{
get { return _selectedIndex > 0; }
}
private ICommand _nextCommand;
public ICommand NextCommand
{
get { return _nextCommand; }
set
{
_nextCommand = value;
NotifyPropertyChanged();
}
}
private ICommand _prevCommand;
public ICommand PrevCommand
{
get { return _prevCommand; }
set
{
_prevCommand = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class DelegateCommand : ICommand
{
private readonly Action _action;
public DelegateCommand(Action action)
{
_action = action;
}
public void Execute(object parameter)
{
_action();
}
public bool CanExecute(object parameter)
{
return true;
}
#pragma warning disable 67
public event EventHandler CanExecuteChanged;
#pragma warning restore 67
}
Then in the XAML, I'd have the following:
<Page
x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.DataContext>
<local:QuestionModel/>
</Page.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<TextBlock x:Name="Question" Text="{Binding QuestionText}"/>
</ScrollViewer>
<TextBlock x:Name="Count" Grid.Row="1" Margin="10,10,10,0" Text="{Binding CurrentQuestion, Mode=OneWay}">
</TextBlock>
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button x:Name="previous" Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10" Content="Previous" IsEnabled="{Binding IsPreviousEnabled}" Command="{Binding PrevCommand}"></Button>
<Button x:Name="next" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10" Content="Next" IsEnabled="{Binding IsNextEnabled}" Command="{Binding NextCommand}"></Button>
</Grid>
</Grid>
</Page>
This way, there is no code in the code behind and you don't have to programatically create controls or modify the content. You just let the framework's data binding do the work for you and your model doesn't need to know how the UI puts everything together and vice versa.

Related

WPF are there preferred ways to do 2D charting without a third party library?

I was wanting to do a project to make a very generic reusable user control for charting that was pretty much formless. I was hoping to get my feet wet in charting but it seems almost EVERYONE on the planet uses WPF Toolkit or Sparrow Charts for free 3rd party ways to make charts. Does anyone have experience in binding or building a totally isolated based way of doing charting? I was thinking of doing something generic to start like binding a polyline in a canvas and passing. I was curious if anyone else had been down this road and had tips for setting up binding for event raising and potential dependent properties. I was thinking of following an MVVM architecture approach and doing most of the binding to a ViewModel but I would ultimately want to expose properties to update.
Similar to this in concept (UserControl to embed in another View or MainForm):
<StackPanel>
<Label x:Name="lblCustomDataGridHeader" Content="{Binding TestText}" HorizontalAlignment="Center" FontSize="24"/>
<Canvas Height="260" Width="300">
<Polyline Points="{Binding Points}" Stroke="LightBlue" StrokeThickness="4" />
</Canvas>
</StackPanel>
ViewModel Properties:
public ViewModel()
{
TestText = "Line Chart";
//Obviously some converter or something else here to accept one or many lines
Points = "0,260 10,250 20,245 40,200 50,250 80, 200, 140,100";
}
public string TestText {
get { return _testText; }
set
{
_testText = value;
OnPropertyChanged(NameOf(TestText));
}
}
private string _points;
public string Points {
get { return _points; }
set
{
_points = value;
OnPropertyChanged(NameOf(Points));
}
}
EDIT LATER
I have also tried doing a templated control that binds to a class
<Style TargetType="{x:Type local:LineGraph}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:LineGraph}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="Hello" FontSize="20"/>
<Border Grid.Row="1" BorderThickness="1" BorderBrush="Black" CornerRadius="15" Margin="10">
<Canvas Margin="10" x:Name="PART_Canvas">
<Canvas.LayoutTransform>
<ScaleTransform ScaleX="1" ScaleY="-1" />
</Canvas.LayoutTransform>
</Canvas>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Class (some of this needs to be cleaned up as I was using someone else's implementation and it was in VB.NET and converted):
public class LineGraph : Control, INotifyPropertyChanged
{
//CONSTRUCTOR
static LineGraph()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(LineGraph), new FrameworkPropertyMetadata(typeof(LineGraph)));
}
public event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged;
public void OnPropertyChanged(string info)
{
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public static readonly DependencyProperty _Trends = DependencyProperty.RegisterReadOnly("Trends", typeof(Collection<ChartDataSegment>), typeof(LineGraph), new PropertyMetadata(new Collection<ChartDataSegment>())).DependencyProperty;
public Collection<ChartDataSegment> Trends {
get { return (Collection<ChartDataSegment>)GetValue(_Trends); }
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
dynamic canvas = GetTemplateChild("PART_Canvas") as Canvas;
if (canvas != null && Trends != null) {
foreach (void trend_loopVariable in Trends) {
trend = trend_loopVariable;
DrawTrend(canvas, trend);
}
}
}
private void DrawTrend(Canvas drawingCanvas, ChartDataSegment Trend)
{
dynamic t = Trend as ChartDataSegment;
if (t != null && t.Points != null) {
for (int i = 1; i <= t.Points.Count - 1; i++) {
dynamic toDraw = new Line {
X1 = t.Points(i - 1).X,
Y1 = t.Points(i - 1).Y,
X2 = t.Points(i).X,
Y2 = t.Points(i).Y,
StrokeThickness = 2,
Stroke = t.LineColor
};
drawingCanvas.Children.Add(toDraw);
}
}
}
}
public class ChartDataSegment : DependencyObject
{
public static readonly DependencyProperty _LineColor = DependencyProperty.Register("LineColor", typeof(Brush), typeof(ChartDataSegment), new PropertyMetadata(null));
public Brush LineColor {
get { return (Brush)GetValue(_LineColor); }
set { SetValue(_LineColor, value); }
}
public static readonly DependencyProperty _LineThickness = DependencyProperty.Register("LineThickness", typeof(Thickness), typeof(ChartDataSegment), new PropertyMetadata(null));
public Thickness PointThickness {
get { return (Thickness)GetValue(_LineThickness); }
set { SetValue(_LineThickness, value); }
}
public static readonly DependencyProperty _Points = DependencyProperty.Register("Points", typeof(ObservableCollection<Point>), typeof(ChartDataSegment), new UIPropertyMetadata(null));
public ObservableCollection<Point> Points {
get { return (ObservableCollection<Point>)GetValue(_Points); }
set { SetValue(_Points, value); }
}
}
And implementation in ViewModel:
var lineTrend1 = new ChartDataSegment {
LineColor = Brushes.Blue,
Points = new ObservableCollection<Point>({
new Point {
X = 1,
Y = 1
},
new Point {
X = 50,
Y = 20
},
new Point {
X = 100,
Y = 100
},
new Point {
X = 150,
Y = 130
}
})
};
var ctrl = new LineGraph();
ctrl.Trends.Add(lineTrend1);
My biggest concern will be not that this can be accomplished but injecting items in on demand not just on instantiation but later after the object is already running to keep updating the lines as needed. EG: Async calls to update a line chart rather than host a static chart that needs to be disposed and then recalled.
My immediate question since Stack Overflow wants specifics on problems is: "Can you easily inject collections(lines) of collections(points) and use a Canvas with a dependent property to self update?"
I made a specific type of graph that I needed, and like you, I did not use any third party library. I was looking for it and I found this project project:
http://www.codeproject.com/Articles/446888/Custom-Spider-or-Radar-Chart-Control
They make a chart similar to the following:
The sources are available for download, and it does not use third-party code so you can easily review all they do.
To create the graph is only necessary to do:
<WpfCharts:SpiderChart Title="Spider chart"
Lines="{Binding Lines}"
Axis="{Binding Axes}"
Minimum="0"
Maximum="1"
Ticks="5"
ShowLegend="True"
LegendLocation="BottomRight"
LegendBackgroundColor="Aquamarine"/>
Which as you can see already it's binding the properties. I'm already using it in a MVVM project.
I hope it will guide you in the way of what you're looking for.
Yes you can create a charting control, a simple one with one kind of "Chart", the real work starts when you want to extend that piece of code, then you have to remember that at some point your requirements can change and the design you started with will not be good anymore. Personally I think this would be a good exercise to take on. But you need to remember that the data you will receive will change, nobody will want to pass Collection of Points, I personally would like to pass a collection of integers or floats or decimals. Pretty much whatever tickles my fancy. At this point you will start to scrach your head and think of another way of implementing all that logic again. That's my friend those few months down the line. I am not trying to discourage you from taking this task but your starting point is bound to change in the near future.
BTW Listen to Will he knows what he is talking about.

Why my text box is not correctly initialized when the datacontext is an list?

I have a Hero class. It has only one property Name and it implemented the interface about the changes.
public class Hero : INotifyPropertyChanged, INotifyCollectionChanged
{
public string Name { get { return _name; }
set
{
_name = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Name"));
}
if (CollectionChanged != null)
{
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace));
}
}
}
private string _name = "";
}
My xaml is follows. I bind the text's datacontext to a collection named Heros which is defined in back code.
<Window x:Class="Chap21_2.TestCollection"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TestCollection" Height="640" Width="480"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBox Grid.Row="1" Text="{Binding Name}" DataContext="{Binding Heros}"></TextBox>
<Button Grid.Row="2" Content="aa" ></Button>
</Grid>
</Window>
And here're my back code. It initlialize the collection, the problem is when the Init() order is changed, the result is different.
public partial class TestCollection : Window
{
public ObservableCollection<Hero> Heros { get { return _heros; } set { _heros = value; } }
private ObservableCollection<Hero> _heros = new ObservableCollection<Hero>();
public TestCollection()
{
// If move Init() here, it'll works.
InitializeComponent();
Init();
}
void Init()
{
Hero hero = new Hero("Bu Lv", 100, 88, 100, 30);
_heros.Add(hero);
hero.HP = 88;
hero = new Hero("Fei Zhang", 100, 88, 100, 30);
hero.HP = 90;
_heros.Add(hero);
}
}
When I start my code , the text box isn't display "Bu Lv" I expected.
But If I move Init() before InitializedComponent(), it works.
Why?
It's probably due to binding a collection to a single textbox. If I change it to use an ItemsControl, the Init can be after InitializeComponent. Here are the things I did.
Hero.cs
public class Hero : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _name = string.Empty;
public string Name
{
get { return _name; }
set
{
_name = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Name"));
}
}
}
}
TestCollection.xaml
This includes your original with some changes for illustration purposes.
<Window x:Class="Chap21_2.TestCollection"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TestCollection" Height="640" Width="480"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<!-- Your original binding -->
<TextBox Grid.Row="0" Text="{Binding Name}" DataContext="{Binding Heros}" />
<!-- My added ItemsControl for example purposes -->
<ItemsControl Grid.Row="1" ItemsSource="{Binding Heros}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Grid.Row="2" Content="aa" ></Button>
</Grid>
</Window>
TestCollection.xaml.cs
Your example didn't include all of the other properties, so I commented them out for the purpose of this example.
public partial class TestCollection : Window
{
public ObservableCollection<Hero> Heros { get { return _heros; } set { _heros = value; } }
private ObservableCollection<Hero> _heros = new ObservableCollection<Hero>();
public TestCollection()
{
// Putting this here allows the collection to populate BEFORE the UI is initialized.
// Init();
InitializeComponent();
// Putting it here is normal. The ItemsControl works, but the single TextBox binding will not.
Init();
}
private void Init()
{
Hero hero;
//Hero hero = new Hero("Bu Lv", 100, 88, 100, 30);
//hero.HP = 88;
hero = new Hero();
hero.Name = "Bu Lv";
_heros.Add(hero);
//hero = new Hero("Fei Zhang", 100, 88, 100, 30);
//hero.HP = 90;
hero = new Hero();
hero.Name = "Fei Zhang";
_heros.Add(hero);
}
}
My suggestion is that if you are wanting to display a single model from a collection, you may want to add a "SelectedHero" or similar property that contains only a single Hero instance. A collection bound to something is usually meant to render all instances in the collection.
Either way you look at it, you can put the Init() before or after IntializeComponent() because you are not directly interacting with the UI.

Bound ObservableCollection doesn't update ListView

I am having difficulties with binding and INotifyPropertyChanged.
I have a ListView with is bound to an ObservableCollection and there is no problem at startup: data is correctly added to the ListView. When I add a new item to the collection, however, it doesn't update the UI. I'm sure the collection contains the object because I added a button to show the whole content of the collection.
Here is my UI code:
<StackPanel>
<Button Content="Show title" Tapped="Button_Tapped"/>
<ListView ItemsSource="{Binding Subscriptions}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.RowSpan="2" Margin="0 0 10 0"
Source="{Binding IconUri.AbsoluteUri}"/>
<TextBlock Grid.Column="1"
Text="{Binding Title.Text}" Style="{StaticResource BaseTextBlockStyle}"/>
<TextBlock Grid.Row="1" Grid.Column="1"
Text="{Binding LastUpdatedTime.DateTime}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
And here is the data context class:
class RssReaderData : INotifyPropertyChanged
{
private string[] acceptContentTypes = {
"application/xml",
"text/xml"
};
private ObservableCollection<SyndicationFeed> _subscriptions;
public ObservableCollection<SyndicationFeed> Subscriptions
{
get { return _subscriptions; }
set { NotifyPropertyChanged(ref _subscriptions, value); }
}
public int SubscriptionsCount
{
get { return Subscriptions.Count; }
}
public RssReaderData()
{
Subscriptions = new ObservableCollection<SyndicationFeed>();
AddFeedAsync(new Uri("http://www.theverge.com/rss/index.xml"));
AddFeedAsync(new Uri("http://blogs.microsoft.com/feed/"));
}
public async Task<bool> AddFeedAsync(Uri uri)
{
// Download the feed at uri
HttpClient client = new HttpClient();
var response = await client.GetAsync(uri);
// Check that we retrieved the resource without error and that the resource has XML content
if (!response.IsSuccessStatusCode || !acceptContentTypes.Contains(response.Content.Headers.ContentType.MediaType))
return false;
var xmlFeed = await response.Content.ReadAsStringAsync();
// Create a new SyndicationFeed and load the XML to it
SyndicationFeed newFeed = new SyndicationFeed();
newFeed.Load(xmlFeed);
// If the title hasn't been set, the feed is invalid
if (String.IsNullOrEmpty(newFeed.Title.Text))
return false;
Subscriptions.Add(newFeed);
return true;
}
#region INotifyPropertyChanged management
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public bool NotifyPropertyChanged<T> (ref T variable, T value, [CallerMemberName] string propertyName = null)
{
if (object.Equals(variable, value)) return false;
variable = value;
NotifyPropertyChanged(propertyName);
return true;
}
#endregion
}
As you can see I implemented the INotifyPropertyChanged interface while I think I shouldn't even have to (the ObservableCollection does that for me). I don't care about notifying the changes in the items I add to my collection, what I need is to notify when a new item is added to it.
I would say that my code is OK as is, but it seems not and I don't see why :-/
Also, while I'm at it, I have two quick questions: what's are the differences between a ListView and a ListBox and between a Grid and a GridView ?
Thank you for you help :-)
EDIT : as requested, here's the code-behind of the page
RssReaderData context = new RssReaderData();
public FeedsPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
}
private async void Button_Tapped(object sender, TappedRoutedEventArgs e)
{
string feedsTitles = "\n";
foreach (var feed in context.Subscriptions)
{
feedsTitles += "\n " + feed.Title.Text;
}
MessageDialog d = new MessageDialog("There are " + context.SubscriptionsCount + " feeds:" + feedsTitles);
await d.ShowAsync();
}
private async void NewFeedSubscribeButton_Tapped(object sender, TappedRoutedEventArgs e)
{
string feedUri = NewFeedUriInput.Text;
if (String.IsNullOrEmpty(feedUri))
return;
if (!Uri.IsWellFormedUriString(feedUri, UriKind.Absolute))
{
MessageDialog d = new MessageDialog("The URL you entered is not valid. Please check it and try again.", "URL Error");
await d.ShowAsync();
return;
}
bool feedSubscribed = await context.AddFeedAsync(new Uri(feedUri));
if (feedSubscribed)
{
NewFeedUriInput.Text = String.Empty;
FeedsPivot.SelectedIndex = 0;
}
else
{
MessageDialog d = new MessageDialog("There was an error fetching the feed. Are you sure the URL is referring to a valid RSS feed?", "Subscription error");
await d.ShowAsync();
return;
}
}
private void FeedsList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (FeedsList.SelectedIndex > -1 && FeedsList.SelectedIndex < context.SubscriptionsCount)
{
Frame.Navigate(typeof(FeedDetailsPage), context.Subscriptions[FeedsList.SelectedIndex]);
}
}
It turned out that you have created two instances of RssReaderData - one in code behind and one in xaml with:
<Page.DataContext>
<DataModel:RssReaderData/>
</Page.DataContext
In this situation the collection to which your ListView is bound to is not the same you refer in the code behind - context.
The simple solution may be to remove above lines from XAML and set DataContext in the code behind:
public FeedsPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
this.DataContext = context;
}
The other case is that it also probably may not update properly the UI, as KooKiz has pointed out - better would be first to create the item, then add it to a collection.
You may also take a look at this question and its answers which deals with problem when item changes inside ObservableCollection.

How to Programmatically create Grid within row of a Grid in Wpf

I am working in WPF -- There is button with click event handler in my application. As i click on button it's event handler generates a new row in grid named as grids. In this new Row i want to add another grid programmatically to add Label, Button and TextBox in this grid in row.
As i executed my code it only generates a texboxes! where labels and button shown once! Here code and image is : Please feel free to ask if my query is not clear to you!
int r =0;
private void button2_Click(object sender, RoutedEventArgs e)
{
TextEdit text1; Button button1; Grid grid1;
grids.RowDefinitions.Add(new RowDefinition());
text1 = new TextEdit();
text1.SetValue(Grid.ColumnProperty, 1);
text1.SetValue(Grid.RowProperty, r);
button1 = new Button();
button1.Content = "Left + " + r;
button1.Click += new RoutedEventHandler(button1_Click);
button1.SetValue(Grid.ColumnProperty, 1);
button1.SetValue(Grid.RowProperty, r);
grid1 = new Grid();
grid1.SetValue(Grid.ColumnProperty, 1);
grids.RowDefinitions.Add(new RowDefinition());
grid1.SetValue(Grid.RowProperty, r);
grids.Children.Add(button1);
grids.Children.Add(text1);
r = r + 1;
}
EDIT
int r =0;
private void button2_Click(object sender, RoutedEventArgs e)
{
db obj = new db();
var query = from p in obj.TableA select p ;
foreach(var a in query.ToList())
{
TextEdit text1; Button button1; Grid grid1;
grids.RowDefinitions.Add(new RowDefinition());
text1 = new TextEdit();
text1.SetValue(Grid.ColumnProperty, 1);
text1.SetValue(Grid.RowProperty, r);
button1 = new Button();
button1.Content = a.name;
button1.Click += new RoutedEventHandler(button1_Click);
button1.SetValue(Grid.ColumnProperty, 1);
button1.SetValue(Grid.RowProperty, r);
grid1 = new Grid();
grid1.SetValue(Grid.ColumnProperty, 1);
grids.RowDefinitions.Add(new RowDefinition());
grid1.SetValue(Grid.RowProperty, r);
grids.Children.Add(button1);
grids.Children.Add(text1);
r = r + 1;}
}
Ok. Delete all your code and start all over.
If you're working with WPF, you really need to have The WPF Mentality
As a general rule, you almost never create or manipulate UI elements in procedural code in WPF.
That's what XAML is for.
This the right way to do what you're asking in WPF (in a full working example):
<Window x:Class="MiscSamples.ItemsControlSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors"
Title="ItemsControlSample" Height="300" Width="300">
<DockPanel>
<Button Content="Add New Row" Command="{Binding AddNewRowCommand}"
DockPanel.Dock="Bottom"/>
<ItemsControl ItemsSource="{Binding Data}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" Background="Gainsboro" BorderThickness="1" Margin="2">
<!-- This is the Inner Grid for each element, which is represented in Brown color in your picture -->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width=".2*"/>
<ColumnDefinition Width=".2*"/>
</Grid.ColumnDefinitions>
<Label Content="{Binding Label1Text}"
Margin="2"/>
<Button Content="Button1"
Command="{Binding DataContext.Command1, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}"
Grid.Column="1" Margin="2"/>
<Button Content="Button2"
Command="{Binding DataContext.Command2, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}"
Grid.Column="2" Margin="2"/>
<dxe:TextEdit Text="{Binding Text}"
Grid.Row="1" Grid.ColumnSpan="3"
Margin="2"/>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<ScrollViewer CanContentScroll="True">
<ItemsPresenter/>
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DockPanel>
</Window>
Code Behind:
public partial class ItemsControlSample : Window
{
public ItemsControlSample()
{
InitializeComponent();
DataContext = new ItemsControlSampleViewModel();
}
}
ViewModel:
public class ItemsControlSampleViewModel
{
public ObservableCollection<ItemsControlSampleData> Data { get; set; }
public Command AddNewRowCommand { get; set; }
public Command<ItemsControlSampleData> Command1 { get; set; }
public Command<ItemsControlSampleData> Command2 { get; set; }
public ItemsControlSampleViewModel()
{
var sampledata = Enumerable.Range(0, 10)
.Select(x => new ItemsControlSampleData()
{
Label1Text = "Label1 " + x.ToString(),
Text = "Text" + x.ToString()
});
Data = new ObservableCollection<ItemsControlSampleData>(sampledata);
AddNewRowCommand = new Command(AddNewRow);
Command1 = new Command<ItemsControlSampleData>(ExecuteCommand1);
Command2 = new Command<ItemsControlSampleData>(ExecuteCommand2);
}
private void AddNewRow()
{
Data.Add(new ItemsControlSampleData() {Label1Text = "Label 1 - New Row", Text = "New Row Text"});
}
private void ExecuteCommand1(ItemsControlSampleData data)
{
MessageBox.Show("Command1 - " + data.Label1Text);
}
private void ExecuteCommand2(ItemsControlSampleData data)
{
MessageBox.Show("Command2 - " + data.Label1Text);
}
}
Data Item:
public class ItemsControlSampleData
{
public string Label1Text { get; set; }
public string Text { get; set; }
}
Helper classes:
public class Command : ICommand
{
public Action Action { get; set; }
public string DisplayName { get; set; }
public void Execute(object parameter)
{
if (Action != null)
Action();
}
public bool CanExecute(object parameter)
{
return IsEnabled;
}
private bool _isEnabled = true;
public bool IsEnabled
{
get { return _isEnabled; }
set
{
_isEnabled = value;
if (CanExecuteChanged != null)
CanExecuteChanged(this, EventArgs.Empty);
}
}
public event EventHandler CanExecuteChanged;
public Command(Action action)
{
Action = action;
}
}
public class Command<T>: ICommand
{
public Action<T> Action { get; set; }
public void Execute(object parameter)
{
if (Action != null && parameter is T)
Action((T)parameter);
}
public bool CanExecute(object parameter)
{
return IsEnabled;
}
private bool _isEnabled = true;
public bool IsEnabled
{
get { return _isEnabled; }
set
{
_isEnabled = value;
if (CanExecuteChanged != null)
CanExecuteChanged(this, EventArgs.Empty);
}
}
public event EventHandler CanExecuteChanged;
public Command(Action<T> action)
{
Action = action;
}
}
Result:
Notice how I'm not dealing with UI in procedural code, but instead I'm using DataBinding and simple, simple properties. That's how you program in WPF. That's what the WPF mentality is about.
I'm using an ItemsControl and a DataTemplate defined in XAML to let WPF create the UI for each of my data items.
Also notice how my code does nothing except expose the data and define reusable Commands that serve as abstractions to the user actions such as Button clicks. This way you can concentrate in coding your business logic instead of struggling with how to make the UI work.
The buttons inside each item are bound to the Commands using a RelativeSource Binding to navigate upwards in the Visual Tree and find the DataContext of the ItemsControl, where the Commands are actually defined.
When you need to add a new item, you just add a new item to the ObservableCollection that contains your data and WPF automatically creates the new UI elements bound to that.
Though this might seem like "too much code", most of the code I posted here is highly reusable and can be implemented in a Generic ViewModel<T> that is then reusable for any type of data items. Command and Command<T> are also write-once reusable classes that can be found in any MVVM framework such as Prism, MVVM Light or Caliburn.Micro.
This approach is really much preferred in WPF, because it enables a great amount of scalability and independence between the UI and the business logic, and it also enables testability of the ViewModel.
I suggest you read all the materials linked in the post, most importantly Rachel's WPF Mentality and related blog posts. Let me know if you need further help.
WPF Rocks. Just copy and paste my code in a File -> New Project -> WPF Application and see the results for yourself.
It's actually much easier in behind code then in xaml code..
My Xaml code:
<Window x:Class="WpfAddGridWithStackPanel.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>
<Grid x:Name="Grid_Grid" Margin="0,0,0,32">
<Grid>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<Grid x:Name="Grid_Grid" Margin="0,0,0,32"/>
</ScrollViewer>
<Button x:Name="btn_Add" Height="32" DockPanel.Dock="Bottom" VerticalAlignment="Bottom" Content="Add New Row" Click="btn_Add_Click" Width="150" HorizontalAlignment="Left" UseLayoutRounding="True" />
<Button x:Name="btn_Remove" Height="32" DockPanel.Dock="Bottom" VerticalAlignment="Bottom" Content="Remove last Row" Click="btn_Remove_Click" Width="150" HorizontalAlignment="Right" />
</Grid>
</Window>
And Code behind:
public partial class MainWindow : Window
{
int num = 0;
public MainWindow()
{
InitializeComponent();
}
void btn1_Click(object sender, RoutedEventArgs e)
{
throw new NotImplementedException();
}
void btn2_Click(object sender, RoutedEventArgs e)
{
throw new NotImplementedException();
}
private void btn_Remove_Click(object sender, RoutedEventArgs e)
{
try
{
Grid_Grid.RowDefinitions.RemoveAt(Grid_Grid.RowDefinitions.Count - 1);
Grid_Grid.Children.RemoveAt(Grid_Grid.Children.Count - 1);
num--;
}
catch { }
}
private void btn_Add_Click(object sender, RoutedEventArgs e)
{
StackPanel stack = new StackPanel();
DockPanel dock = new DockPanel();
Label lbl = new Label();
Button btn1 = new Button();
Button btn2 = new Button();
TextBox txt1 = new TextBox();
stack.Children.Add(dock);
stack.Children.Add(txt1);
dock.Children.Add(lbl);
dock.Children.Add(btn2);
dock.Children.Add(btn1);
#region StackPanel Properties
stack.Background = Brushes.LightGray;
#endregion
#region DockPanel Content Properties
lbl.Content = "Label " + (num + 1).ToString();
lbl.Height = 32;
lbl.Width = 100;
lbl.FontSize = 12;
lbl.SetValue(DockPanel.DockProperty, Dock.Left);
lbl.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
btn1.Content = "Butten 1";
btn1.Height = 32;
btn1.Width = 100;
btn1.FontSize = 12;
btn1.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
btn1.SetValue(DockPanel.DockProperty, Dock.Right);
btn1.Click += new RoutedEventHandler(btn1_Click);
btn2.Content = "Butten 2";
btn2.Height = 32;
btn2.Width = 100;
btn2.FontSize = 12;
btn2.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
btn2.SetValue(DockPanel.DockProperty, Dock.Right);
btn2.Click += new RoutedEventHandler(btn2_Click);
#endregion
#region TextBox Properties
txt1.Text = "Text " + (num + 1).ToString();
txt1.Height = 32;
txt1.Width = double.NaN;
txt1.FontSize = 12;
txt1.Padding = new Thickness(0, 7, 0, 7);
#endregion
Grid_Grid.RowDefinitions.Add(new RowDefinition());
Grid_Grid.RowDefinitions[num].Height = new GridLength(66, GridUnitType.Pixel);
Grid_Grid.Children.Add(stack);
stack.SetValue(Grid.RowProperty, num);
num++;
}
}

View within a view not updating - Caliburn.Micro

I'm having an issue where a datagrid is not reflecting changes to its collection when attached to a view inside a view. More accurately, I have a SecondView within the MainView. On the SecondView I have a datagrid with autogeneratecolumns set to true; when the datagrid is first rendered, it displays the appropriate columns and headers. However, when I populate the list that is attached to it, no changes are reflected.
Here is the complete code for the two views and their respective viewmodels:
MainWindowView:
<Window x:Class="MyApp.MainWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:cal="http://www.caliburnproject.org"
xmlns:views="clr-namespace:MyApp"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindowView" Height="300" Width="300">
<Grid>
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="File">
<MenuItem Header="Open" x:Name="Open"/>
<MenuItem Header="Exit" x:Name="Exit"/>
</MenuItem>
</Menu>
<StackPanel DockPanel.Dock="Bottom">
<views:SecondView/>
</StackPanel>
</DockPanel>
</Grid>
MainWindowViewModel:
namespace MyApp
{
[Export(typeof(IShell))]
internal class MainWindowViewModel : Screen, IShell
{
Regex expression = new Regex(#"^N\d\.C\d\.D\d\.R\d:\s\s\s-\d"); //ex. "N1.C1.D2.R1: -3"
SecondViewModel svm = new SecondViewModel();
public void Open()
{
Microsoft.Win32.OpenFileDialog openFile = new Microsoft.Win32.OpenFileDialog();
openFile.Multiselect = true;
openFile.Filter = "Text Files(*.txt)|*.txt|Log Files(*.log)|*.log|All Files(*.*)|*.*";
openFile.Title = "Open File(s)";
bool? userClickedOK = openFile.ShowDialog();
string[] _fileNames = openFile.FileNames;
if (userClickedOK == true)
{
if (_fileNames != null)
{
for (int i = 0; i < _fileNames.Length; i++)
{
ValidFiles(_fileNames[i]);
}
}
}
}
public void Exit()
{
App.Current.Shutdown();
}
/* ValidFiles() accepts a string containing a filename and creates a Streamreader that reads the file if it is not a Boxboro file.
*/
public void ValidFiles(string filename)
{
string line;
using (StreamReader sr = new StreamReader(filename))
{
while ((line = sr.ReadLine()) != null)
{
if (line.Contains("Mono Status"))
{
Console.WriteLine("File(s) not supported by this parser. Please select a valid file.");
break;
}
else
{
IsMatch(line);
}
}
}
}
/* IsMatch() accepts a string "input" and determines which parsing method to send the string to, if any.
* Strings not matching any of the initial criteria are not processed to limit overhead.
*/
public void IsMatch(string input)
{
Match match = expression.Match(input);
if (match.Success)
{
svm.GetData(input);
}
}
}
}
SecondWindowView:
<UserControl x:Class="MyApp.SecondView"
xmlns:cal="http://www.caliburnproject.org"
cal:Bind.Model="MyApp.SecondViewModel"
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="300">
<Grid>
<StackPanel>
<DataGrid x:Name="MyList"/>
</StackPanel>
</Grid>
SecondWindowViewModel:
namespace MyApp
{
[Export(typeof(SecondViewModel))]
class SecondViewModel:Screen
{
Parse parse = new Parse();
BindableCollection<MyObject> myList = new BindableCollection<MyObject>();
MyObject myObject;
public MyObject MyObject
{
get { return myObject; }
set
{
myObject = value;
NotifyOfPropertyChange(() => MyList);
}
}
public BindableCollection<MyObject> MyList
{
get { return myList; }
set
{
MyList = value;
NotifyOfPropertyChange(() => MyList);
}
}
public void GetData(string input)
{
string[] tempArray = input.Split();
List<int> tempList = new List<int>();
for (int i = 1; i < tempArray.Length; i++)
{
if (!string.IsNullOrEmpty(tempArray[i]))
{
tempList.Add(Convert.ToInt32(tempArray[i]));
}
}
int[] tempIntArray = tempList.ToArray();
MyObject = new MyObject(tempArray[0], tempIntArray[0], tempIntArray[1], tempIntArray[2], tempIntArray[3]);
this.MyList.Add(MyObject);
Console.WriteLine("MyList has " + MyList.Count.ToString() + " elements.");
//foreach (MyObject item in MyList)
//{
// Console.WriteLine(item.Location);
//}
}
}
}
Boostrapper:
namespace MyApp
{
internal class AppBootStrapper : Bootstrapper<IShell>
{
static AppBootStrapper()
{
//Initializes the logger for debugging, remove or comment out in release.
LogManager.GetLog = type => new DebugLogger(type);
}
private CompositionContainer container;
protected override void BuildUp(object instance)
{
this.container.SatisfyImportsOnce(instance);
}
protected override void Configure()
{
this.container =
new CompositionContainer(
new AggregateCatalog(
AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>()));
var batch = new CompositionBatch();
batch.AddExportedValue<IWindowManager>(new WindowManager());
batch.AddExportedValue<IEventAggregator>(new EventAggregator());
batch.AddExportedValue(this.container);
this.container.Compose(batch);
}
protected override IEnumerable<object> GetAllInstances(Type serviceType)
{
return this.container.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType));
}
//This method is required for the BootStrapper.cs to be discovered.
protected override object GetInstance(Type serviceType, string key)
{
string contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key;
IEnumerable<object> exports = this.container.GetExportedValues<object>(contract);
if (exports.Count() > 0)
{
return exports.First();
}
throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
}
}
}
Based on my understanding of Caliburn.Micro, whenever the observablecollection MyList is updated (a new item added), the datagrid with x:name MyList should be updated. Even without a data template, I would think I would see a list of blank entries equivalent in length to the number of objects in MyList. When I use this same code in the MainViewModel, rather than in a usercontrol bound to the MainView, I have no issues rendering the list. It seems I'm missing something about updating a view within a view.
I should note, I can verify the list has objects in it by using Console.WriteLine(MyList.Count.ToString()) and watching the output window. I hate asking about these things, every time I do it ends up being a typo or something equally silly, but I've been stuck here for too long.
NOTE: Even with MyList.Refresh() thrown in on each iteration, no changes in the datagrid occur.
NOTE: It seems like this may answer my question, but I don't understand how to implement it. Perhaps if someone else understands it better, they could put the lines of code in the appropriate places in my code and explain why it works. Thanks in advance. Caliburn.Micro convention-based bindings not working in nested views?
Try this viewmodel first approach - I suspect your inner view isn't being bound (CM doesn't look across control boundaries when applying conventions e.g. it won't apply conventions to nested usercontrols)
<Window x:Class="MyApp.MainWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:cal="http://www.caliburnproject.org"
xmlns:views="clr-namespace:MyApp"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindowView" Height="300" Width="300">
<Grid>
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="File">
<MenuItem Header="Open" x:Name="Open"/>
<MenuItem Header="Exit" x:Name="Exit"/>
</MenuItem>
</Menu>
<StackPanel DockPanel.Dock="Bottom">
<!-- Use ContentControl for sub-views, CM will do it's magic if you bind to the VM property using the standard conventions -->
<ContentControl x:Name="SecondView" />
</StackPanel>
</DockPanel>
</Grid>
Then in your main:
internal class MainWindowViewModel : Screen, IShell
{
Regex expression = new Regex(#"^N\d\.C\d\.D\d\.R\d:\s\s\s-\d"); //ex. "N1.C1.D2.R1: -3"
// Declare your second VM as a property so you can bind to it via CM conventions
public SecondViewModel SecondView
{
get { return _secondView; }
set
{
_secondView = value;
NotifyOfPropertyChange(() => SecondView);
}
}
public MainWindowViewModel()
{
SecondView = new SecondViewModel();
}
CM will automatically inject the right view into the content controls template and setup the datacontext
Alternatively, you can use Bind.Model to bind the VM instance to the view which is more a view-first approach
<StackPanel DockPanel.Dock="Bottom">
<views:SecondView cal:Bind.Model="{Binding SecondView}" />
</StackPanel>
(I think it's Bind.Model and not View.Model but I often get the two mixed up, so failing Bind.Model try View.Model)

Categories

Resources