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)
Related
I am trying to implement MVVM into my software.
What I want: I want a ViewModel.cs (ViewModel) file to substitute for the MainWindow.xaml.cs (MainWindow) file (which should only have InitializeComponent() inside)
What I did: I moved the data from my MainWindow to the newly created ViewModel.
What went wrong: I am having issues binding the MainWindow's XAML file to the ViewModel, with errors being
The name 'comPortList/donglesView' does not exist in the current context
I referenced the following links I considered related to my issue
How do XAML files associate with cs files?
binding property from another cs file with converter WPF
but I came up blank. Is there something I am missing? Please advise, or let me know if I am not providing enough info.
Helpful Data
Relevant MainWindow.xaml code: The bottom three lines (comPortList, btnPortOpen and donglesView) need to work off code in the ViewModel.
<Window x:Class="comPortTesterEX.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:comPortTesterEX"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<!-- -->
<Grid>
<ListBox x:Name="comPortList" SelectionMode="Single" Grid.Row="0" Grid.Column="0" />
<Button x:Name="btnPortOpen" Grid.Row="0" Grid.Column="1" Click="PortOpen_Click" Content ="Open Port"/>
<TreeView x:Name="donglesView" Grid.Row="1" Grid.RowSpan="3" Grid.Column="0" Grid.ColumnSpan="2">
ViewModel Code: the bottom three lines in 1. rely on code here, but I do not know how to link the two.
namespace comPortTesterEX
{
class ViewModel : ObservableObject
{
public ObservableCollection<Dongle> dongles;
DispatcherTimer timer;
public ViewModel()
{
timer = new DispatcherTimer();
timer.Tick += new EventHandler(checkAndUpdateComPortList);
timer.Interval = new TimeSpan(0, 0, 1);
timer.Start();
dongles = new ObservableCollection<Dongle>();
Trace.WriteLine("Started");
donglesView.ItemsSource = dongles;
}
private void checkAndUpdateComPortList(object sender, EventArgs e)
{
List<String> portNames = new List<String>();
foreach (string portName in SerialPort.GetPortNames())
{
portNames.Add(portName);
}
if (SerialPort.GetPortNames().Count() == 0)
{
portNames.Clear();
}
comPortList.ItemsSource = portNames;
}
...
private void PortOpen_Click(object sender, RoutedEventArgs e)
{
bool isComPortInList = false;
//Checks for each highlighted item (limited to one)
foreach (String name in comPortList.SelectedItems)
{
if (dongles.Count() == 0) // If there is nothing in bottom list -> CREATE ONE
{
createDongle(dongles, name, 0);
}
else //If there is already a list
{
for (int i = 0; i < dongles.Count(); i++) // Compare highlighted to EVERY ITEM IN LIST
{
// Check if it already exists in list
if (dongles[i].ComPortName == name)
{
isComPortInList = true;
} // return true if it does
}
if (isComPortInList == false)
{
//Added element is last element, not 0th
createDongle(dongles, name, dongles.Count - 1);
}
}
}
}
}
}
ObservableObject coding was copied from Rachel Lim's MVVM page, link is https://rachel53461.wordpress.com/2011/05/08/simplemvvmexample/
You cannot access donglesView.ItemsSource in a class other than MainWindow (technically you can, but you are not supposed to).
Instead of the private dongles and portNames fields, the view model should expose public readonly properties
public ObservableCollection<Dongle> Dongles { get; }
= new ObservableCollection<Dongle>();
public ObservableCollection<string> PortNames { get; }
= new ObservableCollection<string>();
to which the view bind like this:
<TreeView ItemsSource="{Binding Dongles}" ... />
<ListBox ItemsSource="{Binding PortNames}" ... />
Updates of the collection would look like this:
public void UpdatePortNames()
{
PortNames.Clear();
foreach (string portName in SerialPort.GetPortNames())
{
PortNames.Add(portName);
}
}
You also have to assign an instance of the view model class to the DataContext of the MainWindow, either in XAML
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
or in code
public MainWindow()
{
DataContext = new ViewModel();
InitializeComponent();
}
Turns out you can't totally count MainWindow.xaml.cs out of the equation, you can just minimize its impact and reduce everything to initialize component, and then bind it to something else like App.
I am trying to build a rather complex custom control.
I want to use recursion in my View Model to build out a control. I will try and be as clear as possible.
I have two classes, Publisher and Developer
The Publisher class looks like the following:
public class Publisher
{
public Publisher()
{
SubPublishers.CollectionChanged += SubPublishers_CollectionChanged;
ChildDevelopers.CollectionChanged += SubPublishers_CollectionChanged;
}
private ObservableCollection<Publisher> subPublishers;
public ObservableCollection<Publisher> SubPublishers
{
get
{
if (subPublishers == null)
subPublishers = new ObservableCollection<Publisher>();
return subPublishers;
}
}
private ObservableCollection<Developer> childDevelopers;
public ObservableCollection<Developer> ChildDevelopers
{
get
{
if (childDevelopers == null)
childDevelopers = new ObservableCollection<Developer>();
return childDevelopers;
}
}
And my Developer Class looks like this:
public class Developer : NotifyPropertyChanged
{
public Developer(string Title)
{
this.Title = Title;
}
private string title;
public string Title
{
get
{
return this.title;
}
set
{
this.title = value;
OnPropertyChanged("Title");
}
}
So yes, Publisher is n-tier. It can have a Collection of Developers and each of these Developers can have their own Collection of Developers.
Going to my Main View Model:
public class MainViewModel : NotifyPropertyChanged
{
public MainViewModel()
{
this.ParentPublisher = new ParentPublisher();
BuildData();
}
private Publisher parentPublisher;
public Publisher ParentPublisher
{
get
{
return this.parentPublisher;
}
set
{
this.parentPublisher = value;
OnPropertyChanged("ParentPublisher");
}
}
public void BuildData()
{
Publisher firstPublisher = new Publisher();
firstPublisher.ChildDevelopers.Add(new Developer("HAL"));
firstPublisher.ChildDevelopers.Add(new Developer("Retro Games"));
firstPublisher.ChildDevelopers.Add(new Developer("Nintendo"));
Publisher secondPublisher = new Publisher();
secondPublisher.ChildDevelopers.Add(new Developer("343"));
secondPublisher.ChildDevelopers.Add(new Developer("Playground Games"));
secondPublisher.SubPublishers.Add(new Publisher());
secondPublisher.SubPublishers.FirstOrDefault().ChildDevelopers.Add(new Developer("Coalition"));
secondPublisher.SubPublishers.FirstOrDefault().ChildDevelopers.Add(new Developer("Remedy"));
secondPublisher.SubPublishers.FirstOrDefault().SubPublishers.Add(new Publisher());
secondPublisher.SubPublishers.FirstOrDefault().SubPublishers.FirstOrDefault().ChildDevelopers.Add(new Developer("Insomniac"));
secondPublisher.SubPublishers.FirstOrDefault().SubPublishers.FirstOrDefault().ChildDevelopers.Add(new Developer("Criterion"));
secondPublisher.SubPublishers.FirstOrDefault().SubPublishers.FirstOrDefault().ChildDevelopers.Add(new Developer("EA"));
ParentPublisher.Add(firstPublisher);
ParentPublisher.Add(secondPublisher);
}
}
}
So, you can see the possible scenarios here. Now, I was trying to figure out how to build a control off this data.
I want to actually bind to the ParentPublisher because everything added (SubPublishers and the Child Developers) will ultimately be extension of the Parent.
Would I use an ObservableCollection and use the ItemSource to this ParentPublisher?
Any tips or recommendations would be appreciated.
One way is to make a UserControl that excepts the first tier as a DataTemplate. Then you show the data in the DataTemplate as needed. Inside the DataTemplate reference the UserControl again with the inner data.
Example: Obviously modify the layout to benefit you but for simplicity I did it this way.
<UserControl x:Class="WPF_Question_Answer_App.PublisherView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPF_Question_Answer_App"
mc:Ignorable="d"
d:DesignHeight="450"
d:DesignWidth="800">
<UserControl.Resources>
<DataTemplate x:Key="DeveloperTemplate">
<TextBlock Text="{Binding Title}" /> <!--show the developer data-->
</DataTemplate>
<DataTemplate x:Key="PublisherTemplate">
<local:PublisherView /> <!-- reference ourself for recursion-->
</DataTemplate>
</UserControl.Resources>
<StackPanel>
<ItemsControl ItemsSource="{Binding ChildDevelopers}"
ItemTemplate="{StaticResource DeveloperTemplate}" />
<ItemsControl ItemsSource="{Binding SubPublishers}"
ItemTemplate="{StaticResource PublisherTemplate}" />
</StackPanel>
</UserControl>
First of all, I have two pages called MainPage and BindPage. The MainPage can navigate to the BindPage. The BindPage have two bindings:
One is DataContext Binding in xaml:
<Page x:Class="MvvmlightTest.View.BindingPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MvvmlightTest.View"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:cvt="using:MvvmlightTest.Converter"
DataContext="{Binding BindingVM, Source={StaticResource Locator}, Mode=OneWay}"
Unloaded="Page_Unloaded">
The other binding is in the page content:
<Grid Background="#7F919191"
Visibility="{x:Bind VM.IsInBuyProcess, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}">
<ProgressRing Width="100"
Height="100"
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsActive="True" />
</Grid>
The definition of VM is:
public BindingViewModel VM
{
get
{
return DataContext as BindingViewModel;
}
}
The BindingViewModel has just one property:
private bool isInBuyProcess;
public bool IsInBuyProcess
{
get
{
return true;
}
set
{
isInBuyProcess = value;
}
}
And also I need to show BindPage.g.cs, it contain two important classes: BindingPage_obj1_Bindings & BindingPage_obj1_BindingsTracking
BindingPage_obj1_BindingsTracking is the member of BindingPage_obj1_Bindings
public BindingPage_obj1_BindingsTracking bindingsTracking;
public BindingPage_obj1_Bindings()
{
this.bindingsTracking = new BindingPage_obj1_BindingsTracking(this);
Tk = new WeakReference<BindingPage_obj1_BindingsTracking>(this.bindingsTracking);
}
The BindingPage_obj1_Bindings is initialize here:
public global::Windows.UI.Xaml.Markup.IComponentConnector GetBindingConnector(int connectionId, object target)
{
global::Windows.UI.Xaml.Markup.IComponentConnector returnValue = null;
switch(connectionId)
{
case 1:
{
global::Windows.UI.Xaml.Controls.Page element1 = (global::Windows.UI.Xaml.Controls.Page)target;
BindingPage_obj1_Bindings bindings = new BindingPage_obj1_Bindings();
returnValue = bindings;
bindings.SetDataRoot(this);
bindings.SetConverterLookupRoot(this);
this.Bindings = bindings;
element1.Loading += bindings.Loading;
}
break;
}
return returnValue;
}
And BindingPage_obj1_BindingsTracking could not be released outside of BindingPage_obj1_Bindings
private void StopTracking()
{
this.bindingsTracking.ReleaseAllListeners();
this.initialized = false;
}
private void ReleaseAllListeners()
{
UpdateChildListeners_VM(null);
}
private void UpdateChildListeners_VM(global::MvvmlightTest.ViewModel.BindingViewModel obj)
{
if (obj != cache_VM)
{
if (cache_VM != null)
{
((global::System.ComponentModel.INotifyPropertyChanged)cache_VM).PropertyChanged -= PropertyChanged_VM;
cache_VM = null;
}
if (obj != null)
{
cache_VM = obj;
((global::System.ComponentModel.INotifyPropertyChanged)obj).PropertyChanged += PropertyChanged_VM;
}
}
}
After I navigate From MainPage to BindPage, and then go back with GC collection, the BindPage and BindingPage_obj1_Bindings can be released but BindingPage_obj1_BindingsTracking NOT.
If I move the DataContext binding to code-behind and write it to the Page_Loaded event, BindingPage_obj1_BindingsTracking will be released.
So, does the result mean the x:Bind could not be used with Binding?
btw, my test project here
======================Update======================
After some days, I find the result. The solution is quite simple, just call the Bindings.StopTracking() in the Page_Unloaded method, so all the one-way binding and two way binding should be released.
The solution is not a good way to implement more with MVVM because the Bindings property is private and we need to call the function in the code-behind. I don't quite understand why Microsoft generate the Bindings to private.
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.
I need to create an UI which allows me to select entries from one list box and add it to another listbox at the run time. Now, the listbox1 may contain combo box and checkbox as the items.
For example, if I add a combo box labelled Quarter with values "Q1, Q2, Q3, Q4" as an item in listbox1 and select the entry Q1 in it, and click on the "Add" button, it should be added to listbox2. Vice versa should also be possible. This should be possible at the run time. How could I add combo box and checkbox as an item to the listbox? Also, please suggest if for the add-remove buttons, the code I've is correct.
private void MoveListBoxItems(ListBox source, ListBox destination)
{
ListBox.SelectedObjectCollection sourceItems = source.SelectedItems;
foreach (var item in sourceItems)
{
destination.Items.Add(item);
}
while (source.SelectedItems.Count > 0)
{
source.Items.Remove(source.SelectedItems[0]);
}
}
private void button1_Click(object sender, EventArgs e)
{
MoveListBoxItems(listBox1, listBox2);
}
private void button2_Click(object sender, EventArgs e)
{
MoveListBoxItems(listBox2, listBox1);
}
This is a WPF solution to your need. I am posting it because you told me it could be useful for you. It largely surpasses anything you can ever hope to achieve in winforms, which is a very limited and outdated technology.
This is how it looks in my screen:
I am using some simple ViewModels to represent the data:
ListItemViewModel (the "base" one):
public class ListItemViewModel: ViewModelBase
{
private string _displayName;
public string DisplayName
{
get { return _displayName; }
set
{
_displayName = value;
NotifyPropertyChange(() => DisplayName);
}
}
}
BoolListItemViewModel (for CheckBoxes):
public class BoolListItemViewModel: ListItemViewModel
{
private bool _value;
public bool Value
{
get { return _value; }
set
{
_value = value;
NotifyPropertyChanged(() => Value);
}
}
}
SelectableListItemViewModel (for ComboBoxes):
public class SelectableListItemViewModel: ListItemViewModel
{
private ObservableCollection<ListItemViewModel> _itemsSource;
public ObservableCollection<ListItemViewModel> ItemsSource
{
get { return _itemsSource ?? (_itemsSource = new ObservableCollection<ListItemViewModel>()); }
}
private ListItemViewModel _selectedItem;
public ListItemViewModel SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
NotifyPropertyChange(() => SelectedItem);
}
}
}
This is the "Main" ViewModel, which holds the 2 lists and the Commands (the Button actions)
public class ListBoxSampleViewModel: ViewModelBase
{
private ObservableCollection<ListItemViewModel> _leftItems;
public ObservableCollection<ListItemViewModel> LeftItems
{
get { return _leftItems ?? (_leftItems = new ObservableCollection<ListItemViewModel>()); }
}
private ObservableCollection<ListItemViewModel> _rightItems;
public ObservableCollection<ListItemViewModel> RightItems
{
get { return _rightItems ?? (_rightItems = new ObservableCollection<ListItemViewModel>()); }
}
private DelegateCommand<ListItemViewModel> _moveToRightCommand;
public DelegateCommand<ListItemViewModel> MoveToRightCommand
{
get { return _moveToRightCommand ?? (_moveToRightCommand = new DelegateCommand<ListItemViewModel>(MoveToRight)); }
}
private void MoveToRight(ListItemViewModel item)
{
if (item != null)
{
LeftItems.Remove(item);
RightItems.Add(item);
}
}
private DelegateCommand<ListItemViewModel> _moveToLeftCommand;
public DelegateCommand<ListItemViewModel> MoveToLeftCommand
{
get { return _moveToLeftCommand ?? (_moveToLeftCommand = new DelegateCommand<ListItemViewModel>(MoveToLeft)); }
}
private void MoveToLeft(ListItemViewModel item)
{
if (item != null)
{
RightItems.Remove(item);
LeftItems.Add(item);
}
}
}
This is the entire XAML for the Window:
<Window x:Class="WpfApplication4.Window14"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication4"
Title="Window14" Height="300" Width="300">
<Window.Resources>
<DataTemplate DataType="{x:Type local:ListItemViewModel}">
<TextBlock Text="{Binding DisplayName}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:BoolListItemViewModel}">
<CheckBox Content="{Binding DisplayName}" IsChecked="{Binding Value}" HorizontalAlignment="Left"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:SelectableListItemViewModel}">
<ComboBox ItemsSource="{Binding ItemsSource}" SelectedItem="{Binding SelectedItem}"
HorizontalAlignment="Stretch" MinWidth="100"/>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="100"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox ItemsSource="{Binding LeftItems}"
x:Name="LeftList"/>
<StackPanel Grid.Column="1" VerticalAlignment="Center">
<Button Content="Move to Right"
Command="{Binding MoveToRightCommand}"
CommandParameter="{Binding SelectedItem,ElementName=LeftList}"/>
<Button Content="Move to Left"
Command="{Binding MoveToLeftCommand}"
CommandParameter="{Binding SelectedItem,ElementName=RightList}"/>
</StackPanel>
<ListBox ItemsSource="{Binding RightItems}"
Grid.Column="2" x:Name="RightList"/>
</Grid>
</Window>
and finally, this is the Window Code-behind, which only initializes the ViewModel with some items:
public partial class Window14 : Window
{
public Window14()
{
InitializeComponent();
DataContext = new ListBoxSampleViewModel()
{
LeftItems =
{
new ListItemViewModel(){DisplayName = "Item1"},
new BoolListItemViewModel() {DisplayName = "Check Item 2", Value = true},
new SelectableListItemViewModel()
{
ItemsSource =
{
new ListItemViewModel() {DisplayName = "Combo Item 1"},
new BoolListItemViewModel() {DisplayName = "Check inside Combo"},
new SelectableListItemViewModel()
{
ItemsSource =
{
new ListItemViewModel() {DisplayName = "Wow, this is awesome"},
new BoolListItemViewModel() {DisplayName = "Another CheckBox"}
}
}
}
}
}
};
}
}
At first glance, this might seem like a LOT of code... but if you take 2 seconds to analyze it... Its just "simple, simple properties and INotifyPropertyChanged. That's how you program in WPF.
I'm talking about a completely different paradigm from what you might be used to in winforms, but it's really worth the effort of learning it. Notice that nowhere in my code I am interacting with UI elements. I just create the ViewModel structure and let the WPF Binding System to take care of generating the UI for me, using the provided DataTemplates.
I'm using the ViewModelBase from MVVM Light and the DelegateCommand from WPFTutorial.net. You can copy and paste my code in a File -> New Project -> WPF Application and see the results for yourself (you will also need these 2 classes from the links above)
If you need to integrate this in an existing winforms application, you will need the ElementHost