I'm new in Silverlight and I'm just trying to get a handle on how to do some things. What I'd like to do is have a chart, that has 2 lines that might overlap one another. What is happening with my code is that one line is displayed and then the next line is displayed, one after another rather than overlapping.
Here is my xaml
<navigation:Page x:Class="SilverlightNav1.Home"
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:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
xmlns:charting="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="547"
Title="Home"
Style="{StaticResource PageStyle}" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit">
<Grid x:Name="LayoutRoot" Height="544">
<sdk:Label Height="28" HorizontalAlignment="Left" Margin="12,12,0,0" Name="label1" VerticalAlignment="Top" Width="120" Content="First Name" />
<TextBox Height="23" Margin="91,8,0,0" Name="txtFirstName" VerticalAlignment="Top" HorizontalAlignment="Left" Width="120" >
<TextBox.RenderTransform>
<SkewTransform AngleX="2" AngleY="12" />
</TextBox.RenderTransform>
<TextBox.Effect>
<DropShadowEffect />
</TextBox.Effect>
</TextBox>
<Button Content="Greet Me" Height="23" HorizontalAlignment="Left" Margin="91,72,0,0" Name="GreetMe" VerticalAlignment="Top" Width="75" Click="GreetMe_Click" />
<TextBlock Margin="91,116,25,0" Name="Greeting" Text="" Height="105" VerticalAlignment="Top" FontSize="20" />
<toolkit:Chart HorizontalAlignment="Left" Margin="21,246,0,0" Name="chart1" Title="Testing graph" VerticalAlignment="Top" Height="249" Width="400">
<toolkit:Chart.Series>
<toolkit:LineSeries Title="Guys" ItemsSource="{Binding p}" IndependentValueBinding="{Binding FirstName}" DependentValueBinding="{Binding Greeting}" Name="testGraph">
</toolkit:LineSeries>
<toolkit:LineSeries Title="Girls" ItemsSource="{Binding girls}" IndependentValueBinding="{Binding FirstName}" DependentValueBinding="{Binding Greeting}" Name="lsGirls">
</toolkit:LineSeries>
</toolkit:Chart.Series>
</toolkit:Chart>
</Grid>
Here is my code-behind.
List<Model.Person> p = new List<Model.Person>();
p.Add(new Model.Person() { FirstName = "Bob", Greeting = 1 });
p.Add(new Model.Person() { FirstName = "Phil", Greeting = 3 });
p.Add(new Model.Person() { FirstName = "Dennis", Greeting = 4});
p.Add(new Model.Person() { FirstName = "Chewie", Greeting = 2 });
//this.DataContext = p;
List<Model.Person> girls = new List<Model.Person>();
girls.Add(new Model.Person() { FirstName = "Jamie", Greeting = 4 });
girls.Add(new Model.Person() { FirstName = "Abby", Greeting = 7 });
girls.Add(new Model.Person() { FirstName = "M", Greeting = 5 });
girls.Add(new Model.Person() { FirstName = "K", Greeting = 6 });
((LineSeries)chart1.Series[0]).ItemsSource = p;
((LineSeries)chart1.Series[1]).ItemsSource = girls;
And finally here is my class defined in person.cs
namespace SilverlightNav1.Model
{
public class Person
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
}
}
private int _greeting;
public int Greeting
{
get { return _greeting; }
set { _greeting = value; }
}
}
}
An issue is that both data sets have different X-axis values - Bob and Jamie are not the same, for example, so the toolkit will not show them on the same X-axis value. You can see how this could be fixed by changing the girls data set FirstName's to Bob, Phil, Dennis, and Chewie.
One option is to assign a increasing numeric counter to both guys and girls sets, and use that as the X-axis, so that they can be graphed together. So have the X-axis be bound to the "Counter" property, and have bob/phil/dennis/chewie have respective values of 0,1,2, and 3, and the same for Jamie through K. You could style the graph to display the actual FirstName next to the datapoint itself, or another such method.
Related
I have a listview inside another listview, but the second listview is not respecting the automatic height, apparently it is based on some property of the parent listview as the image.
Listview
-First listview, I'm using a templateselector of QuestoesTemplates
<ListView x:Name="ListView_ListaPerguntas"
ItemsSource="{Binding Perguntas}"
SeparatorColor="Black"
HasUnevenRows="True"
SeparatorVisibility="Default"
ItemTapped="ListView_ListaPerguntas_ItemTapped"
ItemTemplate="{StaticResource QuestoesTemplates}"
IsEnabled="True">
</ListView>
-secont listView in of template selector
<DataTemplate x:Key="MultiplaTemplate">
<ViewCell>
<FlexLayout Direction="Column" Margin="0,10" BackgroundColor="DarkOrchid">
<Label Text="{Binding Ds_DescricaoPergunta}"/>
<ListView x:Name="ListView_AlternativaSelecao"
HasUnevenRows="True"
ItemsSource="{Binding Path=Alternativas}"
BackgroundColor="Blue" Margin="20"
VerticalOptions="Fill">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<FlexLayout Direction="Row">
<Label Text="{Binding Ds_DescricaoPergunta}" Margin="10,0"/>
<Label Text=" - "/>
<CheckBox />
</FlexLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.Header>
<StackLayout VerticalOptions="EndAndExpand" Orientation="Horizontal">
<Label Text="Confirma as alternativas?" HorizontalOptions="StartAndExpand"/>
<Button Text="Confirmar" HorizontalOptions="EndAndExpand"/>
</StackLayout>
</ListView.Header>
<ListView.Footer>
<StackLayout VerticalOptions="EndAndExpand" Orientation="Horizontal">
<Label Text="Confirma as alternativas?" HorizontalOptions="StartAndExpand"/>
<Button Text="Confirmar" HorizontalOptions="EndAndExpand"/>
</StackLayout>
</ListView.Footer>
</ListView>
<StackLayout VerticalOptions="EndAndExpand" Orientation="Horizontal">
<Label Text="Confirma as alternativas?" HorizontalOptions="StartAndExpand"/>
<Button Text="Confirmar" HorizontalOptions="EndAndExpand"/>
</StackLayout>
<BoxView HeightRequest="2" BackgroundColor="Red"/>
</FlexLayout>
</ViewCell>
</DataTemplate>
and the result is the image, he occupying a higher height.
Based on your desired functionality, I would break up each questions into pages, or you can use a horizontal scrolling carousel view. You could even have a button to submit answer and go to the next one.
Like Jason said, you could use a Grouped ListView. I make a simple code for you reference.
Xaml:
<ListView IsGroupingEnabled="true" ItemsSource="{Binding ListOfAnswers}">
<ListView.GroupHeaderTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding Questions}" BackgroundColor="Purple"/>
</ViewCell>
</DataTemplate>
</ListView.GroupHeaderTemplate>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding Ds_DescricaoPergunta}"/>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Code behind:
public partial class MainPage : ContentPage
{
private List<AnswerList> _listOfAnswers;
public List<AnswerList> ListOfAnswers { get { return _listOfAnswers; } set { _listOfAnswers = value; base.OnPropertyChanged(); } }
public MainPage()
{
InitializeComponent();
var Q1List = new AnswerList()
{
new Answer(){ Ds_DescricaoPergunta="Q1_Answer1"},
new Answer(){ Ds_DescricaoPergunta="Q1_Answer2"},
new Answer(){ Ds_DescricaoPergunta="Q1_Answer3"},
};
Q1List.Questions = "Q1";
var Q2List = new AnswerList()
{
new Answer(){ Ds_DescricaoPergunta="Q2_Answer1"},
new Answer(){ Ds_DescricaoPergunta="Q2_Answer2"},
new Answer(){ Ds_DescricaoPergunta="Q2_Answer3"},
};
Q2List.Questions = "Q2";
var Q3List = new AnswerList()
{
new Answer(){ Ds_DescricaoPergunta="Q3_Answer1"},
new Answer(){ Ds_DescricaoPergunta="Q3_Answer2"},
};
Q3List.Questions = "Q3";
var Q4List = new AnswerList()
{
new Answer(){ Ds_DescricaoPergunta="Q4_Answer1"},
new Answer(){ Ds_DescricaoPergunta="Q4_Answer2"},
new Answer(){ Ds_DescricaoPergunta="Q4_Answer3"},
new Answer(){ Ds_DescricaoPergunta="Q4_Answer4"},
};
Q1List.Questions = "Q4";
var list = new List<AnswerList> { Q1List, Q2List, Q3List, Q4List };
ListOfAnswers = list;
this.BindingContext = this;
}
}
public class Answer
{
public string Ds_DescricaoPergunta { get; set; }
}
public class AnswerList : List<Answer>
{
public string Questions { get; set; }
public List<Answer> Answers => this;
}
Screenshot:
I searched a lot for the solution to this, but can't seem to figure it out. (Just saying, this will be a long post)
Preface: This is NOT MVVM
Problem: I have a progress bar. I am currently trying to bind a public double to it from within the xaml's .cs file. However, whenever I use value's bind function, it never works, resulting in iNotifyPropertyChanged not working, etc. etc....
The variable it is bound to serves the purpose of updating the progress bar value. The way that is completed is by setting the value in a separate for loop in another method/class. The value is set by a simple equation. 100 divided by the length of a list, multiplied by the for loops counter. This gives us a real number that can be put into the progress bar value. (code below)
Binder.daWindow.progUpdate = ((100.00 / databaseTables.Length) * dbTIndex);
I know this can be completed by threading (or whatever), but I would appreciate answers specifically for how this might be done by binding (unless it's impossible, then forget what I just said).
XAML:
<Window x:Name="DateWindow1" x:Class="WpfApp1.DateWindow"
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:WpfApp1"
xmlns:Main="using:DateWindow"
mc:Ignorable="d"
Title="Change Dates" Height="232.667" Width="462" Background="#FF2B2B2B" ResizeMode="NoResize">
<Grid>
<TextBox x:Name="DayChangeTextBox" HorizontalAlignment="Left" Height="30" Margin="176,45,0,0" TextWrapping="Wrap" Text="##" VerticalAlignment="Top" Width="58" FontSize="14" TextAlignment="Center" AcceptsReturn="False" AcceptsTab="False" GotFocus="DayChangeTextBox_GotFocus" LostFocus="DayChangeTextBox_LostFocus" BorderThickness="1" BorderBrush="Black" OpacityMask="Black" Background="#FFFDFDFD"/>
<Label Content="Days" HorizontalAlignment="Left" Margin="239,45,0,0" VerticalAlignment="Top" Width="42" FontSize="14" FontFamily="Microsoft YaHei UI" Height="30" Foreground="White"/>
<Label Content="Move Dates Forward:" HorizontalAlignment="Left" Margin="23,45,0,0" VerticalAlignment="Top" Width="144" FontSize="14" FontFamily="Microsoft JhengHei UI Light" Height="30" Foreground="White"/>
<Button x:Name="ChangeDatesButton" Content="Change Dates" HorizontalAlignment="Left" Margin="303,45,0,0" VerticalAlignment="Top" Width="143" Height="30" BorderThickness="1,1,2,3" BorderBrush="#FF474747" Click="ChangeDatesButton_Click" FontFamily="Microsoft YaHei UI" Background="#FFE6E6E6"/>
<Label x:Name="serverLabel" Content="Label" HorizontalAlignment="Left" Margin="10,73,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.5,0.5" Width="5" Height="15" Visibility="Hidden"/>
<Label x:Name="databaseLabel" Content="Label" HorizontalAlignment="Left" Margin="20,73,0,0" VerticalAlignment="Top" Width="3" Visibility="Hidden" Height="15"/>
<Button x:Name="RevertToDefaultButton" Content="Revert to Backup" Margin="23,164,0,0" VerticalAlignment="Top" Height="30" Background="#FFE6E6E6" BorderBrush="#FF474747" BorderThickness="1,1,2,3" Click="RevertToDefaultButton_Click" FontFamily="Microsoft JhengHei UI" HorizontalAlignment="Left" Width="143"/>
<Button x:Name="OverwriteDefaultButton" Content="Backup Database" Margin="23,128,0,0" VerticalAlignment="Top" Height="30" BorderBrush="#FF474747" BorderThickness="1,1,2,3" Click="OverwriteDefaultButton_Click" FontFamily="Microsoft JhengHei UI" HorizontalAlignment="Left" Width="143" Background="#FFE6E6E6" />
<Button x:Name="CloseButton" Content="Close" Margin="0,164,10,0" VerticalAlignment="Top" Height="30" BorderBrush="#FF474747" BorderThickness="1,1,2,3" FontFamily="Microsoft JhengHei UI" Width="60" Click="CloseButton_Click" HorizontalAlignment="Right" Background="#FFE6E6E6" />
<Label x:Name="RevertBackupLabel" Content="Backup or Revert Database:" HorizontalAlignment="Left" Margin="12,93,0,0" VerticalAlignment="Top" Foreground="White" FontFamily="Microsoft JhengHei UI" FontWeight="Bold" FontSize="16"/>
<Label x:Name="DatesLabel" Content="Change Dates in Database:" Margin="12,10,0,0" Foreground="White" FontFamily="Microsoft JhengHei UI" FontWeight="Bold" FontSize="16" HorizontalAlignment="Left" Width="216" Height="30" VerticalAlignment="Top"/>
<ProgressBar HorizontalAlignment="Left" Height="13" Margin="303,80,0,0" VerticalAlignment="Top" Width="143" Name="pbStatus" Value="{Binding local.progUpdate, UpdateSourceTrigger=PropertyChanged}" Maximum="{Binding mx}" Minimum="{Binding mn}" LargeChange="0.01"/>
</Grid>
XAML CS Portion:
public static class Binder
{
public static DateWindow daWindow;
}
public partial class DateWindow : Window, INotifyPropertyChanged
{
private double _progUpdate;
public double progUpdate
{
get { return _progUpdate; }
set
{
_progUpdate = value;
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(progUpdate)));
}
}
}
public double mx = 100;
public double mn = 0;
public event PropertyChangedEventHandler PropertyChanged;
public DateWindow()
{
InitializeComponent();
Binder.daWindow = this;
ChangeDateInDatabase ChangeDate = new ChangeDateInDatabase();
progUpdate = 25; // This is there so we can test the variable without going through the whole program.
}
private void ChangeDatesButton_Click(object sender, RoutedEventArgs e)
{
ChangeDateInDatabase ChangeDate = new ChangeDateInDatabase();
var result = MessageBox.Show("Are you sure you would like to change dates for " + Globals.selectedDatabase + " from server " + Globals.main.ServerComboBox.Text + "?", "Change Dates?", MessageBoxButton.YesNo, MessageBoxImage.Exclamation);
if (Globals.main.ServerComboBox.Text != "" && Globals.selectedDatabase != "" && FindDatabaseAndServer.Connector(Globals.main.ServerComboBox.Text, Globals.selectedDatabase) && result == MessageBoxResult.Yes)
{
try
{
long.Parse(DayChangeTextBox.Text);
ChangeDate.ChangeDates(this);
//ChangeDateInDatabase.ChangeDates(this);
}
catch
{
MessageBox.Show($"Invalid number {DayChangeTextBox.Text}, please enter a valid number.", "ERROR", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
(CLICK function serves the purpose of calling a method for the program. Inside the method, its main loop, which determines the methods completion, contains the variable desired to be bound with a defining equation for its value)
Method Containing Said Loop:
for (int dbTIndex = 0; dbTIndex < databaseTables.Length; dbTIndex++)
{
string[] dateColumns = new string[0];
string columnSelectQuery = "SELECT DATA_TYPE, COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '" + databaseTables[dbTIndex].ToString() + "' AND COLUMN_NAME LIKE '_%'";
using (SqlConnection connection2 = new SqlConnection(connectionString))
{
SqlCommand command2 = new SqlCommand(columnSelectQuery, connection2);
connection2.Open();
SqlDataReader reader2 = command2.ExecuteReader();
int y = 0;
while (reader2.Read())
{
if (reader2[0].ToString() == "datetime")
{
Array.Resize(ref dateColumns, dateColumns.Length + 1);
dateColumns[y] = reader2[1].ToString();
y++;
}
}
}
for (var dtColIndex = 0; dtColIndex < dateColumns.Length; dtColIndex++)
{
string addDatesQuery = "UPDATE " + databaseTables[dbTIndex] + " SET " + dateColumns[dtColIndex] + " = DATEADD(dd," + Convert.ToInt32(date.DayChangeTextBox.Text) + "," + dateColumns[dtColIndex] + ") WHERE " + dateColumns[dtColIndex] + " IS NOT NULL";
using (SqlConnection connection3 = new SqlConnection(connectionString))
{
SqlCommand command3 = new SqlCommand(addDatesQuery, connection3);
connection3.Open();
command3.ExecuteNonQuery();
}
}
//vvvvv VARIABLE THAT NEEDS TO BE BOUND vvvvvvv
Binder.daWindow.progUpdate = ((100.00 / databaseTables.Length) * dbTIndex);
}
(There is obviously more code to this part, which I can provide if needed)
Thanks in advance!
EDIT:
For the XAML, local.progUpdate is not the only thing I have tried. I've done path, progUpdate by itself, etc.
Your ProgressBar's first binding makes no sense: {Binding local.progUpdate, UpdateSourceTrigger=PropertyChanged}. You're not specifying a source, and there's no DataContext assigned anywhere, so the binding won't evaluate to anything because the path is meaningless. Because the property is on the window itself, you can just assign the window's data context to itself.
// DateWindow.xaml.cs
public DateWindow()
{
InitializeComponent();
DataContext = this;
...
}
<!-- DateWindow.xaml -->
<ProgressBar Value="{Binding progUpdate, Mode=OneWay}" ... />
Your other bindings will require mx and mn to be properties rather than fields.
I want to have a user control[say UC1] comprising 4 text boxes [say tb1,tb2,tb3, and tb4]. This user control should have 4 normal properties [say prop1, prop2, prop3, and prop4] binding to these text boxes. I want a dependency property [say dp] exposed to outer world by this user control.
This user control gets a single string [say 0\abc|1\def|2\ghi|3\jkl] from a property[say StrProp] of class [say C1] and is splitted into 4 parts[say abc, def, ghi, and jkl] to display in 4 text boxes of my user control. If any changes done by user in any or all textboxes, all the changed texts should be combined and reflected back to class C1\StrProp property.
Also, my requirement is that dp should be bounded to StrProp in UI\XAML. Validations should also be done properly.
Can anyone please help me by writing an example?
Sample classes are as below:
MyMultiTextBoxUserControl.xaml
<UserControl x:Class="MyMultiTextBoxControl_UsingNConsuming.MyMultiTextBoxUserControl"
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">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height=".25*"/>
<RowDefinition Height=".25*"/>
<RowDefinition Height=".25*"/>
<RowDefinition Height=".25*"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Text="{Binding ElementName=UserControl, Path=CombinedField1 }"/>
<TextBox Grid.Row="1" Text="{Binding ElementName=UserControl, Path=CombinedField2}"/>
<TextBox Grid.Row="2" Text="{Binding ElementName=UserControl, Path=CombinedField3}"/>
<TextBox Grid.Row="3" Text="{Binding ElementName=UserControl, Path=CombinedField4}"/>
</Grid>
</UserControl>
MyMultiTextBoxUserControl.xaml.cs
public partial class MyMultiTextBoxUserControl : UserControl
{
public MyMultiTextBoxUserControl()
{
InitializeComponent();
}
//static FrameworkPropertyMetadata propertydata = new FrameworkPropertyMetadata("Hello",
// FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(PropertyChanged_Callback), new CoerceValueCallback(CoerceValue_Callback),
// false, UpdateSourceTrigger.LostFocus);
//public static readonly DependencyProperty CombinedTextProperty =
// DependencyProperty.Register("CombinedText", typeof(string), typeof(MyMultiTextBoxUserControl), propertydata, new ValidateValueCallback(Validate_ValueCallback));
static FrameworkPropertyMetadata propertydata = new FrameworkPropertyMetadata("Hello",
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(PropertyChanged_Callback));
public static readonly DependencyProperty CombinedTextProperty =
DependencyProperty.Register("CombinedText", typeof(string), typeof(MyMultiTextBoxUserControl), propertydata);
private static bool Validate_ValueCallback(object value)
{
string str=value as string;
bool result = true;
if (str.Length > 28)
result = false;
if (str.Length < 1)
result = false;
if (str.Substring(0, 2) != "0'\'")
result = false;
if (str.Contains("1'\'") == false || str.Contains("2'\'") || str.Contains("3'\'"))
result = false;
return result;
}
private static object CoerceValue_Callback(DependencyObject obj,object value)
{
return value;
}
private static void PropertyChanged_Callback(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
MyMultiTextBoxUserControl control=(MyMultiTextBoxUserControl)obj;
string select = e.NewValue.ToString();
char[] pipeDelim,slashDelim;
string[] pipeSplt;
pipeDelim = new char[] { '|' };
slashDelim = new Char[] { '/' };
pipeSplt = select.Split(pipeDelim);
if (pipeSplt.Length == 1)
return;
string[][] str = new string[4][];
int x = 0;
foreach (string s in pipeSplt)
{
if (string.IsNullOrEmpty(s) == false)
{
str[x] = s.Split(slashDelim);
x++;
}
}
control.CombinedField1 = str[0][1];
control.CombinedField2 = str[1][1];
control.CombinedField3 = str[2][1];
control.CombinedField4 = str[3][1];
}
public string CombinedText
{
get { return GetValue(CombinedTextProperty) as string; }
set { SetValue(CombinedTextProperty, value); }
}
public string CombinedField1
{
get; set;
}
public string CombinedField2
{
get;
set;
}
public string CombinedField3
{
get;
set;
}
public string CombinedField4
{
get;
set;
}
}
CombinedStringClass.cs
namespace MyMultiTextBoxControl_UsingNConsuming
{
public class CombinedStringClass
{
public CombinedStringClass() { }
string m_CombinedString;
public string CombinedString
{
get { return m_CombinedString; }
set
{
if (m_CombinedString != value)
m_CombinedString = value;
}
}
}
}
ConsumerClass.xaml
<Window x:Class="MyMultiTextBoxControl_UsingNConsuming.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyMultiTextBoxControl_UsingNConsuming;assembly="
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:CombinedStringClass x:Key="myClass"/>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*"/>
<ColumnDefinition Width="0.5*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.33*"/>
<RowDefinition Height="0.34*"/>
<RowDefinition Height="0.33*"/>
</Grid.RowDefinitions>
<TextBlock Text="User Control Text Boxes" Grid.Row="0" Grid.Column="0" Foreground="Black" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<local:MyMultiTextBoxUserControl Grid.Row="0" Grid.Column="1" Foreground="Black" CombinedText="{Binding Source=myClass, Path=CombinedString, Mode=TwoWay,FallbackValue=DataNotBound}"/>
<TextBlock Text="Combied String" Grid.Row="2" Grid.Column="0" Foreground="Black" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<TextBox Name="OneStringTextBox" Grid.Row="2" Grid.Column="1" Foreground="Black" Text="0\abc|1\def|2\ghi|3\jkl" IsEnabled="False"/>
</Grid>
</Window>
I also need to combine the changed texts ofUserControl's textboxes in such a way that it should be in a form of [0\f|1\gh|2\zx|3\oo] to be reflected in OneStringTextBox. Also, total string length should be 28 & max length of each textbox is 7.
Read WPF in C# 2010: Windows Presentation Foundation in .NET 4 Matthew MacDonald Chapter 18.
There is a great example that shoud help you.
Give name to your User control, replace {Binding ElementName=UserControl... with {Binding ElementName=NameOfUserControl, convert CombinedFields properties to DPs.
Initially I wanted to pass an "ObservableCollection< customClass>" between two VMs , but even simple messaging isn't working for me.
MainViewModel
private void openNewSpecialCustomer()
{
GalaSoft.MvvmLight.Messaging.Messenger.Default.Send("Musaab");
Console.WriteLine("send done");
AddNewSpecialCustomer a = new AddNewSpecialCustomer();
_dialogService.showDialoge(a);
}
AddNewSpecialCustomerViewModel
public AddNewSpecialCustomerViewModel()
{
GalaSoft.MvvmLight.Messaging.Messenger.Default.Register<string>(this, doSomething);
Console.WriteLine("Should now Receive");
validProperties = new Dictionary<string, bool>();
validProperties.Add("specialCustomerName",false);
validProperties.Add("tel", false);
allPropertiesValid = false;
}
public void doSomething(string s)
{
Console.WriteLine("Should be received");
specialCustomerName = s;
Console.WriteLine("s value " + s);
}
public String specialCustomerName
{
get { return _specialCustomerName; }
set
{
if (_specialCustomerName != value)
{
_specialCustomerName = value;
OnPropertyChanged("specialCustomerName");
}
}
}
now XAML for AddNewSpecialCustomer
<Window FlowDirection="RightToLeft" x:Class="GlassStore.AddNewSpecialCustomer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="clr-namespace:GlassStore.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="AddNewSpecialCustomer" Height="318" Width="458">
<Window.DataContext>
<local:AddNewSpecialCustomerViewModel/>
</Window.DataContext>
<Grid Background="{DynamicResource NormalBrush}">
<Button Command="{Binding Save}" Content="موافق" Height="29" HorizontalAlignment="Left" Margin="31,218,0,0" Name="button1" VerticalAlignment="Top" Width="75" />
<Label Content="إسم العميل" Height="27" HorizontalAlignment="Left" Margin="12,12,0,0" Name="label1" VerticalAlignment="Top" Width="120" />
<TextBox Text="{Binding specialCustomerName,Mode=TwoWay,ValidatesOnDataErrors=True,ValidatesOnExceptions=True,UpdateSourceTrigger=PropertyChanged}" Height="27" HorizontalAlignment="Left" Margin="155,12,0,0" Name="textBox1" VerticalAlignment="Top" Width="210" />
<Label Content="المنطقة/المكان" Height="27" HorizontalAlignment="Left" Margin="12,67,0,0" Name="label2" VerticalAlignment="Top" Width="120" />
<TextBox Text="{Binding region}" Height="27" HorizontalAlignment="Left" Margin="155,67,0,0" Name="textBox2" VerticalAlignment="Top" Width="210" />
<TextBox Text="{Binding tel,ValidatesOnDataErrors=True,ValidatesOnExceptions=True,UpdateSourceTrigger=PropertyChanged}" Height="27" HorizontalAlignment="Left" Margin="155,119,0,0" Name="textBox3" VerticalAlignment="Top" Width="210" />
<Label Content="رقم الهاتف " Height="27" HorizontalAlignment="Left" Margin="12,119,0,0" Name="label3" VerticalAlignment="Top" Width="120" />
<Button Content="إلغاء" Height="29" HorizontalAlignment="Left" Margin="143,218,0,0" Name="button2" VerticalAlignment="Top" Width="75" />
<Label Content="" Height="29" HorizontalAlignment="Left" Margin="12,177,0,0" Name="label4" VerticalAlignment="Top" Width="412" />
</Grid>
you can see that there is textBox, with a Text property bounded to specialCustomerName Property , which I'm trying to change it via the messenger , the data-bind mode is TwoWay, so I expect my textBox to have my name on it when loaded , (I'm sending my name via the messenger) which is not the case, I hope this may be more clear code
thanks in advance
Is your second window constructor called after the first one ?
You should first register and only then send the message. Previously sent messages are not received. Sorry for being C.O. but this kind of mistake is rather possible :)
As both the sending of the message and the receiving of the message happens in constructors it could lead to a race condition. Register for the receiving of the message in the constructor, but send the message at a later point in time, e.g. in the load or a command handler.
Edit:
Could not reproduce the behaviour, here is the code I used to test this:
ViewLocator:
public class ViewModelLocator
{
private static MainViewModel _main;
public ViewModelLocator() {
CreateMain();
}
public static MainViewModel MainStatic {
get {
if (_main == null) {
CreateMain();
}
return _main;
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public MainViewModel Main {
get {
return MainStatic;
}
}
public static void ClearMain() {
if (_main != null) {
_main.Cleanup();
_main = null;
}
}
public static void CreateMain() {
if (_main == null) {
_main = new MainViewModel();
}
}
#region [SecondViewModel]
private static SecondViewModel _secondViewModel;
public static SecondViewModel SecondViewModelStatic {
get {
if (_secondViewModel == null) {
CreateSecondViewModel();
}
return _secondViewModel;
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public SecondViewModel SecondViewModel {
get {
return SecondViewModelStatic;
}
}
public static void ClearSecondViewModel() {
if (_secondViewModel != null) {
_secondViewModel.Cleanup();
_secondViewModel = null;
}
}
public static void CreateSecondViewModel() {
if (_secondViewModel == null) {
_secondViewModel = new SecondViewModel();
}
}
#endregion
public static void Cleanup() {
ClearMain();
ClearSecondViewModel();
}
}
MainViewModel
public class MainViewModel : ViewModelBase
{
public MainViewModel() {
if (IsInDesignMode) {
// Code runs in Blend --> create design time data.
} else {
// Code runs "for real"
}
Messenger.Default.Send("Initializer - does not show becaus of race condition!");
}
public string Welcome {
get {
return "Welcome to MVVM Light";
}
}
#region [TestCommand]
private RelayCommand _cmdTest;
public RelayCommand TestCommand {
get {
return _cmdTest ?? (
_cmdTest = new RelayCommand(
() => {
// Execute delegate
Messenger.Default.Send("Hello!");
}
)
);
}
}
#endregion
public override void Cleanup() {
// Clean up if needed
base.Cleanup();
}
}
SecondViewModel
public SecondViewModel() {
Messenger.Default.Register<string>(this, (s) => this.DoSomething(s));
if (IsInDesignMode) {
// Code runs in Blend --> create design time data.
} else {
// Code runs "for real": Connect to service, etc...
}
}
#region [Message]
public const string MessagePropertyName = "Message";
private string _message = default(string);
public string Message {
get {
return _message;
}
set {
if (_message == value) {
return;
}
_message = value;
RaisePropertyChanged(MessagePropertyName);
}
}
#endregion
public void DoSomething(string s) {
this.Message = s;
}
public override void Cleanup() {
base.Cleanup();
}
}
MainWindow XAML
<Window x:Class="MvvmLightTests.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Height="300"
Width="300"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Skins/MainSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<StackPanel>
<StackPanel>
<TextBlock FontSize="36"
FontWeight="Bold"
Foreground="Purple"
Text="{Binding Welcome}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
TextWrapping="Wrap" />
<Button Content="click to send message" Margin="0,40,0,0" Command="{Binding TestCommand}" />
</StackPanel>
<StackPanel DataContext="{Binding SecondViewModel, Source={StaticResource Locator}}" Margin="0,40,0,0">
<TextBlock Text="{Binding Message, TargetNullValue='--'}" FontWeight="Bold" HorizontalAlignment="Center"/>
</StackPanel>
</StackPanel>
</Grid>
</Window>
After struggling for about one week with a problem in Silverlight 4 + MVVM-Light toolkit and after searching the web without success I want to present my problem here and hope that somebody could give me some hints.
I want to present the simplified programm:
My model classes:
Person
public class Person
{
private decimal _cod_Person;
public decimal Cod_Person
{
get { return _cod_Person; }
set { _cod_Person = value; }
}
private string _des_Person;
public string Des_Person
{
get { return _des_Person; }
set { _des_Person = value; }
}
}
PersonInfo
public class PersonInfo
{
private decimal _cod_person;
public decimal Cod_person
{
get { return _cod_person; }
set { _cod_person = value; }
}
private string _des_note;
public string Des_note
{
get { return _des_note; }
set { _des_note = value; }
}
}
Here my ViewModel:
public class PersonViewModel : ViewModelBase
{
public RelayCommand<Model.PersonInfo> save_Click { get; private set; }
public PersonViewModel()
{
save_Click = new RelayCommand<Model.PersonInfo>(personInfo =>
{
SavePerson(personInfo);
});
//the content of the combo box is defined
AllPerson = new ObservableCollection<Model.Person>
{
new Model.Person(){
Cod_Person = 1,
Des_Person = "Name 1"
},
new Model.Person(){
Cod_Person = 2,
Des_Person = "Name 2"
}
};
//an empty PersonInfo is created, which the UI will work on
ChoosenPerson = new Model.PersonInfo();
}
private void SavePerson(Model.PersonInfo personInfo)
{
//here some safing processing could be done...
//but is omitted here
//and here a new PersonInfo is assigned the ChoosenPerson
ChoosenPerson = new Model.PersonInfo();
}
public const string AllPersonPropertyName = "AllPerson";
private ObservableCollection<Model.Person> _allPersons = null;
public ObservableCollection<Model.Person> AllPerson
{
get
{
return _allPersons;
}
set
{
if (_allPersons == value)
{
return;
}
var oldValue = _allPersons;
_allPersons = value;
RaisePropertyChanged(AllPersonPropertyName, oldValue, value, true);
}
}
public const string ChoosenPersonPropertyName = "ChoosenPerson";
private Model.PersonInfo _choosenPerson = null;
public Model.PersonInfo ChoosenPerson
{
get
{
return _choosenPerson;
}
set
{
if (_choosenPerson == value)
{
return;
}
var oldValue = _choosenPerson;
_choosenPerson = value;
// Update bindings and broadcast change using GalaSoft.MvvmLight.Messenging
RaisePropertyChanged(ChoosenPersonPropertyName, oldValue, value, true);
}
}
}
In my view (PersonView) I have a combo box, a text box and a button:
<UserControl x:Class="TEST_STACKOVERFLOW.PersonView"
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"
mc:Ignorable="d"
DataContext="{Binding Source={StaticResource Locator}, Path=PersonViewModel.ChoosenPerson}" d:DesignHeight="258" d:DesignWidth="341">
<Grid>
<ComboBox Height="23" HorizontalAlignment="Left" Margin="84,51,0,0" Name="comboBox1" VerticalAlignment="Top" Width="120" ItemsSource="{Binding Source={StaticResource Locator}, Path=PersonViewModel.AllPerson}" DisplayMemberPath="Des_Person" SelectedValuePath="Cod_Person" SelectedValue="{Binding Path=Cod_person, Mode=TwoWay}" />
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="159,199,0,0" Name="button1" VerticalAlignment="Top" Width="75" Command="{Binding Source={StaticResource Locator}, Path=PersonViewModel.save_Click}" CommandParameter="{Binding}" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="94,107,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" Text="{Binding Path=Des_note, Mode=TwoWay}" />
</Grid>
When the button is clicked the first time everything works. The parameter (PersonInfo) passed after the button was clicked contains the selected ComboBox value (Cod_person) and the inserted text(Des_note). In the method SavePerson a new PersonInfo instance is assigned to the ChoosenPerson object. The problem occurs when I click the button a second time. Then as parameter after the button was clicked I get an instance of the PersonInfo class which contains the correct inserted text in the text box but as value selected in the combo box I always get 0, independet of what I choosed in the combo box. This problems occurs just in the case, that I use as combo box items instances of a class. If I use as combo box items just string values this problem does not occur. But I have to use instances of a class in my combo box.
I hope that somebody has a hint.
Thanks!
PS:
Interesting is still the fact, that when changing the assignment of the ChoosenPerson from "ChoosenPerson = new Model.PersonInfo();" to _choosenPerson = new Model.PersonInfo(); this means by assignment through use of private members instead of access methods, the second time the button is clicked, the values are written into the parameter of the button correctly. The only thing is that the values that were inserted the last time are not deleted. They are shown after the first button click. But they are not shown when the assignment of a new empty ChoosenPerson is made throuh access methods...
I can't explain this behavior. Who can help me?? Thank you.
Adam was on the right track. I would assign your PersonViewModel to the DataContext instead of assigning it to the ChoosenPerson.
<UserControl
x:Class="TEST_STACKOVERFLOW.PersonView"
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"
DataContext="{Binding Source={StaticResource Locator}, Path=PersonViewModel}"
mc:Ignorable="d" d:DesignHeight="258" d:DesignWidth="341">
<Grid>
<ComboBox Height="23" HorizontalAlignment="Left" Margin="84,51,0,0" Name="comboBox1" VerticalAlignment="Top" Width="120" ItemsSource="{Binding AllPerson}" DisplayMemberPath="Des_Person" SelectedValuePath="Cod_Person" SelectedItem="{Binding Path=ChoosenPerson, Mode=TwoWay}" />
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="159,199,0,0" Name="button1" VerticalAlignment="Top" Width="75" Command="{Binding save_Click}" CommandParameter="{Binding SelectedItem, ElementName=comboBox1}" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="94,107,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" Text="{Binding ChoosenPerson.Des_Person, Mode=TwoWay}" />
</Grid>
</UserControl>
I would also totally get rid of the PersonInfo class. You do not need this class. You can work with the Person object directly. Change your references from PersonInfo to the Person class. Also, if you want to clear out the current selection in the ComboBox, set the ChoosenPerson property to null. Do not new up an instance of a class. The ChoosenPerson property needs to either be null or one of the objects in the AllPerson collection.
public class PersonViewModel : ViewModelBase
{
public RelayCommand<Person> save_Click { get; private set; }
public PersonViewModel()
{
save_Click = new RelayCommand<Person>(personInfo =>
{
SavePerson(personInfo);
});
//the content of the combo box is defined
AllPerson = new ObservableCollection<Person>
{
new Person(){
Cod_Person = 1,
Des_Person = "Name 1"
},
new Person(){
Cod_Person = 2,
Des_Person = "Name 2"
}
};
//an empty PersonInfo is created, which the UI will work on
ChoosenPerson = new Person();
}
private void SavePerson(Person personInfo)
{
//here some safing processing could be done...
//but is omitted here
//and here a new PersonInfo is assigned the ChoosenPerson
ChoosenPerson = null;
}
public const string AllPersonPropertyName = "AllPerson";
private ObservableCollection<Person> _allPersons = null;
public ObservableCollection<Person> AllPerson
{
get
{
return _allPersons;
}
set
{
if (_allPersons == value)
{
return;
}
var oldValue = _allPersons;
_allPersons = value;
RaisePropertyChanged(AllPersonPropertyName, oldValue, value, true);
}
}
public const string ChoosenPersonPropertyName = "ChoosenPerson";
private Person _choosenPerson = null;
public Person ChoosenPerson
{
get
{
return _choosenPerson;
}
set
{
if (_choosenPerson == value)
{
return;
}
var oldValue = _choosenPerson;
_choosenPerson = value;
// Update bindings and broadcast change using GalaSoft.MvvmLight.Messenging
RaisePropertyChanged(ChoosenPersonPropertyName, oldValue, value, true);
}
}
}
I think you want something like this:
<UserControl x:Class="TEST_STACKOVERFLOW.PersonView"
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"
mc:Ignorable="d"
DataContext="{Binding Source={StaticResource Locator}, Path=PersonViewModel}" d:DesignHeight="258" d:DesignWidth="341">
<Grid>
<ComboBox Height="23" HorizontalAlignment="Left" Margin="84,51,0,0" Name="comboBox1" VerticalAlignment="Top" Width="120" ItemsSource="{Binding AllPerson}" DisplayMemberPath="Des_Person" SelectedValuePath="Cod_Person" SelectedValue="{Binding Path=ChoosenPerson, Mode=TwoWay}" />
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="159,199,0,0" Name="button1" VerticalAlignment="Top" Width="75" Command="{Binding save_Click}" CommandParameter="{Binding ChoosenPerson}" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="94,107,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" Text="{Binding Path=Des_note, Mode=TwoWay}" />
</Grid>
I updated most of the bindings. Once you set the UserControl's data context, the rest of the controls inside can simply reference the property name, instead of having to use the Locator. I also think you had a few things pointing to the wrong places.