How do I access elements in a WPF popup? - c#

I want to change the color of a button in a popup based on certain conditions and I want to set some text based on those conditions. I need to do this in the code behind.
I have a popup with several TextBlocks in a StackPanel. The first 3 are bound to details about the course (this is a scheduling app; school project). The last one I want to be empty unless there is a conflict concerning that course. That is, I want to dynamically decide what, if anything, goes in the TextBlock each time the popup is opened.
<Popup Name="CourseListDetail" Width="Auto" Height="Auto">
<StackPanel>
<TextBlock Margin="10,10,10,0">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}: {1}">
<Binding Path="CourseCode"/>
<Binding Path="LongTitle"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock Margin="10,0,10,0">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} - {1}/{2}">
<Binding Path="ProfessorsString"/>
<Binding Path="Enrollment"/>
<Binding Path="Capacity"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock Margin="10,0,10,0" Grid.Row="2" Grid.Column="1" Text="{Binding MeetingsString}" TextWrapping="Wrap"
HorizontalAlignment="Center"/>
<TextBlock TextWrapping="WrapWithOverflow" MaxWidth="300" Text="{Binding Description}" Margin="10,10,10,10"/>
<TextBlock Name="ConflictText" Foreground="Red" HorizontalAlignment="Center" Text="{Binding ConflictString}"/>
<Button Name="Detail_AddCourse" Content="Add To Schedule" Margin="10,10,10,10" Padding="5" Background="LightGreen"
Click="AddCourseButton_Click"
Style="{StaticResource MyButtonStyle}"/>
</StackPanel>
</Border>
</Popup>
I have a function that opens the popup when you click on a course and that gives the popup the DataContext about the course, but I don't know how to access the TextBlock, or the button immediately below it, through the function. I figured there'd be a child property or something so I could call the button, something like:
CourseListDetail.Detail_AddCourse.Background = "Red";
or
CourseListDetail.Child.Button().Background = "Red";
etc.
Code behind function:
private void CourseListItem_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
ListViewItem selection = sender as ListViewItem;
Course course = selection.DataContext as Course;
CourseListDetail.DataContext = course;
CourseListDetail.PlacementTarget = selection;
CourseListDetail.IsOpen = true;
CourseListDetail.Focus();
hasConflict conflictType = _schedule.HasConflict(course);
if (conflictType != hasConflict.NO_CONFLICT) { //If there is a conflict
//Change button color to red here
if (conflictType == hasConflict.COURSE_FULL) { //If the course is full
//Set TextBlock text to conflict message here
}
}
else { //No conflict
//Set button color to green
}
}
hasConflict is just an enum

Use x:Name instead of Name. Then you will be able to access the element in the code behind. See In WPF, what are the differences between the x:Name and Name attributes? for an explanation.

Related

Setting custom ToolTip for Border Control in WPF

I am working on internationalizing a WPF application. I am stuck at a situation where a ToolTip to be set near a border control which is to be concatenated like this:
RoleType : Manager
I am binding RoleType in ViewModel which has Manager, TechLead, Engineer etc. RoleType string is fixed and is translated in Resources file. Now in view, I need to concatenate and show the user who has logged in.
I tried MultiBinding but after login, my screen turns blackout. I cannot exit my application.
Here is the code I am trying:
<StackPanel Grid.Row="0" Orientation="Vertical" VerticalAlignment="Top">
<Border BorderBrush="{Binding BorderBrush}" HorizontalAlignment="Center" VerticalAlignment="Center" Height="auto" Width="auto"
Background="Transparent" >
<Border.ToolTip>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} : {1}">
<Binding Path="Text" ElementName="{languageTranslation:Translate RoleType}" />
<Binding Path="Text" ElementName="{languageTranslation:Translate {Binding UserName}}" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Border.ToolTip>
<ContentControl Height="65" VerticalContentAlignment="Top" HorizontalAlignment="Center" ContentTemplate="{StaticResource UserProfileIcon}"></ContentControl>
</Border>
What am I doing wrong? Converters are not required as it is only strings.

WPF ToolTip Placement Target returns null

I have this code:
<DataTemplate>
<Border>
<Border.ToolTip>
<ToolTip IsEnabled="True"
Placement="Right">
<ToolTip.VerticalOffset>
<MultiBinding Converter="{StaticResource OffsetConverter}"
ConverterParameter="Vertical">
<MultiBinding.Bindings>
<Binding RelativeSource="{RelativeSource Self}"/>
</MultiBinding.Bindings>
</MultiBinding>
</ToolTip.VerticalOffset>
<TextBlock Margin="0"
Padding="0"
TextAlignment="Left"
TextWrapping="Wrap"
MaxWidth="200"
Text="{Binding Description}" FontStyle="Italic">
</TextBlock>
</ToolTip>
</Border.ToolTip>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch" Margin="0">
<Image Height="16" Width="16" Style="{StaticResource AutoCompletionImageStyle}" Margin="0"/>
<Label Content="{Binding DisplayText}" Margin="0" Padding="0"/>
</StackPanel>
</Border>
</DataTemplate>
And this converter :
public object Convert(object[] values, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
AutoCompletionViewModel.OffsetType offsetType = (AutoCompletionViewModel.OffsetType)Enum.Parse(typeof(AutoCompletionViewModel.OffsetType), parameter.ToString());
ToolTip tooltip = values[0] as ToolTip;
Border border = tooltip.PlacementTarget as Border;
double aCalculatedOffset = 0.0;
return aCalculatedOffset;
}
I need to calculate the offset of the tooltip based on some properties of the Border. The tooltip appears relative to the border and manually changing the offset works as expected. However when I try to access the PlacementTarget property of the ToolTip in the converter it is null...
So, although the ToolTip is a direct child of the Border and should have it as a placement target, in the code it is null. Any idea what I am missing here?
I also tried passing the border with <Binding RelativeSource="{RelativeSource AncestorType={x:Type Border}}"/> to no avail.
I am really confused since, if the border is not the logical or visual parent of the ToolTip how does the ToolTip places itself correctly?!
I managed to find a workaround :
<DataTemplate>
<Border ToolTipOpening="OnAutoCompletionBorderToolTipOpening">
<Border.ToolTip>
<ToolTip>
<ToolTip.Style>
<Style>
<EventSetter Event="ToolTip.Opened" Handler="ToolTipOpenedHandler" />
</Style>
</ToolTip.Style>
</ToolTip>
</Border.ToolTip>
</Border>
</DataTemplate>
Code behind:
private void ToolTipOpenedHandler(object sender, RoutedEventArgs e)
{
ToolTip toolTip = (ToolTip)sender;
UIElement target = toolTip.PlacementTarget;
var rect = toolTip.PlacementRectangle;
}
The target here is not null, rather the Border...
I am pretty sure this is some WPF bug. Hopefully someone will be helped by this post.

bind user control to contentcontrol validation rules not firing

I have many user controls and i have main window which contains a content control and list box when user select from list box i bound the selected user control to the content control the problem is when the user control bound for the first time the validation rules is appears but when user control bound for the second time the validation rules disappear what is the problem ?
here is the main window :
<Grid>
<ListBox x:Name="FormsListBox" Margin="0,5,0,5" Grid.Row="1" Grid.Column="0" Width="180" ItemsSource="{Binding Formlist}" SelectedItem="{Binding SelectedFormInfo}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="Resources/Capture.PNG" ></Image>
<TextBlock Text="{Binding Path=FormName}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Grid.Row="1" Grid.Column="1" x:Name="MainStackPanel" >
<ContentControl Content="{Binding Path=SelectedFormInfo.MainForm}">
</ContentControl>
</StackPanel>
</Grid>
here is the sample of validation role in user control:
<StackPanel Width="315" Margin="20,0,10,0">
<TextBox x:Name="TxtWeaponNumber" Width="315">
<TextBox.Text>
<Binding Path="SelectedWeapon.weaponNumber" Mode="TwoWay"
UpdateSourceTrigger="PropertyChanged" ValidatesOnNotifyDataErrors="True"
ValidatesOnDataErrors="True" NotifyOnValidationError="True"
NotifyOnSourceUpdated="True" NotifyOnTargetUpdated="True">
<Binding.ValidationRules>
<validationRules:RequiredRule ValidatesOnTargetUpdated="True" ValidationStep="RawProposedValue" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</StackPanel>
Note: MainForm in SelectedFormInfo contains thee selected user control
i set my user control in AdornerDecorator and the Validation did not disappear

Updating the source of a Listbox with a multibinding

I'm trying to get a multi-bound listbox to call the convert back method when items inside it change. Specifically I have a datatemplate that maps each entry to a checkbox and I'd like the source to be updated whenever a box is checked.
<ListBox x:Name="listBoxEdit" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="3" Height="100" >
<ListBox.ItemsSource>
<MultiBinding Converter="{StaticResource selectedLoadsConverter}" >
<Binding Path="AvailableLoads"/>
<Binding Path="CurrentUnit"/>
</MultiBinding>
</ListBox.ItemsSource>
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Item1}" IsChecked="{Binding Item2}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I know the problem lies in the fact that I'm binding to to the ItemsSource property which is never actually being updated by the checkbox changing, but I can't figure out a good way to push the updates up to the source.

Dynamically control the number of buttons in WPF

I'm working with WPF recently. Now, i'm facing a problem.
I have a button "ADD", every time click on this will add a new row with some contents. Those contents are shown below-
<TextBox Text="{Binding Name}" Margin="10,10" Height="20" ></TextBox>
<TextBox Text="{Binding City}" Margin="10,10" Height="20" ></TextBox>
<TextBox Text="{Binding Age}" Margin="10,10" Height="20"></TextBox>
<TextBox Text="{Binding Count}" Margin="10,10" Height="20" ></TextBox>
<Button Content="M1" Margin="10,10" Height="20"/>
<Button Content="M2" Margin="10,10" Height="20"/>
<Button Content="M3" Margin="10,10" Height="20"/>
</WrapPanel>
</Grid>
Here, at the end there are three buttons M1,M2,M3. But, I don't need this all three buttons every time. I may need only M1 or only M2 or only M3 or M1,M2 etc.
How can I do this in c#?
Actually i don't even know, am i in the right way?
Thanks in advance.
I would highly recommend looking into the MVVM design pattern when working with WPF
That said, I would bind my XAML to an ObservableCollection<SomeObject>, and clicking the AddButton would add a new SomeObject to the ObservableCollection. This would make the UI automatically add the new row when the collection gets updated, and SomeObject could have properties for IsM1Visible, IsM2Visible, and IsM3Visible which determines which buttons are visible.
For example,
Class SomeObject would have
string Name;
string City;
int Age;
int Count;
bool IsM1Visible;
bool IsM2Visible;
bool IsM3Visible;
The XAML would look something like this:
<ItemsControl ItemsSource="{Binding SomeCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBox Text="{Binding Name}" Margin="10,10" Height="20" ></TextBox>
<TextBox Text="{Binding City}" Margin="10,10" Height="20" ></TextBox>
<TextBox Text="{Binding Age}" Margin="10,10" Height="20"></TextBox>
<TextBox Text="{Binding Count}" Margin="10,10" Height="20" ></TextBox>
<Button Content="M1" Visibility="{Binding IsM1Visible, Converter="{StaticResource BooleanToVisibilityConverter}" Margin="10,10" Height="20"/>
<Button Content="M2" Visibility="{Binding IsM2Visible, Converter="{StaticResource BooleanToVisibilityConverter}" Margin="10,10" Height="20"/>
<Button Content="M3" Visibility="{Binding IsM3Visible, Converter="{StaticResource BooleanToVisibilityConverter}" Margin="10,10" Height="20"/>
</WrapPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
And the Add Button's Click event would look something like this:
void AddButton_Click(object sender, EventArgs e)
{
var newItem = new SomeItem
{
Name = "Something",
City = "Something",
Age = 30,
Count = 2,
IsM1Visible = true,
IsM2Visible = false,
IsM3Visible = true
};
SomeCollection.Add(newItem);
}
You could use DataBinding if the amount of buttons is dependent on the size of a list/collection.
<ItemsControl ItemsSource={Binding Ms}>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}" Command={Binding ThingToDoWhenClickedCommand}/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
This will generate exactly the amount of buttons that is in the Ms collection/List that is in the DataContext.
You can just add the buttons through your code-behind files and put an if structure around the buttons to determine what button should be loaded.
Are you using MVVM?
If so then this is easy. Just create a ButtonViewModel object that represents one of your buttons and expose an ObservableCollection of them from your main ViewModel.
In your view just have a ListView, or just an ItemsControl bound to the collection, and a DataTemplate that turns your ButtonViewModel into a button.
When the user clicks the Add button, add a new ButtonViewModel to your collection and the view will update itself to match.
First, let create a view model for a row. In that model you will have 3 bool properties, IsButton1Visible, IsButton2Visible, IsButton3Visible, or something like that with all properties you need to do binding on your row.
Second, your scenario is that when you click Add, new row will be added. So you have a list of Row_View_Model. On AddCommand, you will at a new Row_View_Model into the list. Here you have full control on what button you want to show.

Categories