I made a color-picker with one image(HUE color) and a slider (brightness level).
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.8*"/>
<ColumnDefinition Width="0.2*"/>
</Grid.ColumnDefinitions>
<Border x:Name="borderColorChart" Grid.Column="0">
<Grid>
<Image Stretch="Fill" Source="Assets/colorChart.PNG" MouseDown="Image_MouseDown" MouseMove="Image_MouseMove"/>
<Ellipse x:Name="colorMarker" Width="5" Height="5" StrokeThickness="1" Stroke="#FF0B0B0B"/>
</Grid>
</Border>
<Border x:Name="brightnessSliderBorder" Background="{DynamicResource BrightnessGradient}" Grid.Column="1">
<Grid>
<Slider x:Name="brightnessSlider" Orientation="Vertical" IsMoveToPointEnabled="True" Focusable="False" Minimum="0.0" Maximum="1.0" Style="{DynamicResource SliderStyle}" />
</Grid>
</Border>
</Grid>
This color-picker is contained in a Popup which open when I click on a toggle button :
<ToggleButton x:Name="SelectColorChannel1" Grid.Row="0" Background="{Binding SelectedCurrentColor, ElementName=Channel1Color}">
<Popup IsOpen="{Binding IsChecked, ElementName=SelectColorChannel1}" StaysOpen="True">
<CMiX:ColorPicker x:Name="Channel1Color"/>
</Popup>
</ToggleButton>
Here is the code for the mousemove :
private void Image_MouseDown(object sender, MouseButtonEventArgs e)
{
var cb = new CroppedBitmap((BitmapSource)(((Image)e.Source).Source), new Int32Rect((int)Mouse.GetPosition(e.Source as Image).X, (int)Mouse.GetPosition(e.Source as Image).Y, 1, 1));
_pixels = new byte[4];
cb.CopyPixels(_pixels, 4, 0);
UpdateCurrentColor();
UpdateMarkerPosition();
UpdateSlider();
}
private void Image_MouseMove(object sender, MouseEventArgs e)
{
if (Mouse.LeftButton == MouseButtonState.Pressed)
{
if (e.Source.GetType().Equals(typeof(Image)))
{
var cb = new CroppedBitmap((BitmapSource)(((Image)e.Source).Source), new Int32Rect((int)Mouse.GetPosition(e.Source as Image).X, (int)Mouse.GetPosition(e.Source as Image).Y, 1, 1));
_pixels = new byte[4];
cb.CopyPixels(_pixels, 4, 0);
UpdateMarkerPosition();
UpdateCurrentColor();
Mouse.Synchronize();
UpdateSlider();
}
}
}
And here is the function to update the marker position
private void UpdateMarkerPosition()
{
_markerTransform.X = Mouse.GetPosition(borderColorChart).X - (borderColorChart.ActualWidth / 2);
_markerTransform.Y = Mouse.GetPosition(borderColorChart).Y - (borderColorChart.ActualHeight / 2);
}
The problem is, I can't slide the marker on the image, I can only "click" once to move it, this problem doesn't happen if the color-picker is contained in a ContextMenu. But I need popup, so it stays open while finding the right color on the image and while using the slider.
Any hint ? Thank you
EDIT 1 ---
I have done some testing and now as far as I understand the function UpdateMarkerPosition() on MouseMove is not working when I use a popup, but it is working in case I use a contextmenu... Still, UpdateMarkerPosition() is working on MouseDown
EDIT 2 ---
Ok so now I know precisely that this condition :
if (Mouse.LeftButton == MouseButtonState.Pressed)
is never true in case I use wpf Popup
Take out Popup control from within ToggleButton. And place it separately outside with its Placement property set to Mouse.
Related
I have a RichTextBox and a button. I want to get one char from the RichTextBox and change the color of it every time when user click the button.
I read a lot about TextRange but I can't do it in the right way.
var textentered = txtuser.Text.Substring(txtuser.SelectionStart-1, 1);
var tmptext = txtprogramtext.Substring(txtuser.SelectionStart-1, 1 );
var rangeOfText1 = new TextRange(txtprogram.CaretPosition.GetNextContextPosition(LogicalDirection.Forward),
txtprogram.CaretPosition.GetNextContextPosition(LogicalDirection.Forward));
The following code fragment colors one character in the RichTextBox from the current caret position:
private void Button_Click(object sender, RoutedEventArgs e)
{
// Case 1: Color one character from the current caret position
var startPosition = rtb.CaretPosition;
// Forward to next character
var endPosition = rtb.CaretPosition.GetPositionAtOffset(1, LogicalDirection.Forward);
var tr = new TextRange(startPosition, endPosition);
tr.ApplyPropertyValue(TextElement.ForegroundProperty, Brushes.Red);
rtb.CaretPosition = endPosition; // Forward the caret
//// Case 2: The following line will color the current selected text
//rtb.Selection.ApplyPropertyValue(TextElement.ForegroundProperty, Brushes.Red);
}
The XAML:
<Window ... >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<RichTextBox Grid.Row="0" x:Name="rtb" AllowDrop="True" VerticalScrollBarVisibility="Auto" Padding="2">
<FlowDocument>
<Paragraph FontSize="12" TextAlignment="Left">
<Paragraph.Foreground>
<SolidColorBrush Color="Blue"/>
</Paragraph.Foreground>
<Run Text="RichTextBox has built-in handling for the bubbling MouseUp and MouseDown events. " />
</Paragraph>
<Paragraph FontSize="12" TextAlignment="Left" >
<Paragraph.Foreground>
<SolidColorBrush Color="Black"/>
</Paragraph.Foreground>
<Run Text="If you need to respond to these events, listen for the tunneling PreviewMouseUp and PreviewMouseDown events instead..." />
</Paragraph>
</FlowDocument>
</RichTextBox>
<Button Grid.Row="1" Click="Button_Click">Color</Button>
</Grid>
</Window>
I'm fairly new at coding and i'm struggling with a popup that doesn't appear at runtime.
I'm trying to make an autocomplete/suggestion popup list but i can't seem to be able to make it work.
Here's my XAML:
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="130"/>
<ColumnDefinition x:Name="editorInputColumn"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Ajouter (séparateur ';') :">
<TextBlock.Foreground>
<SolidColorBrush Color="{DynamicResource FontColor}"/>
</TextBlock.Foreground>
</TextBlock>
<Grid x:Name="popupEditorGrid" Grid.Column="1" Visibility="Visible">
<Popup Placement="Top" Visibility="Visible" StaysOpen="True" Panel.ZIndex="1000" x:Name="EditorPopup" Grid.Column="1" Width="{Binding Path=ActualWidth, ElementName=editorInputColumn}">
<StackPanel x:Name="EditorPopupStackPanel">
<StackPanel.Background>
<SolidColorBrush Color="{DynamicResource EllipseSecondary}"/>
</StackPanel.Background>
<TextBlock Text="test"/><!--this is just an attempt at displaying something in the popup, but even this does not appear at runtime-->
</StackPanel>
</Popup>
</Grid>
<TextBox Grid.Column="1" KeyUp="editorAddInput_KeyUp" x:Name="editorAddInput" >
<TextBox.BorderBrush>
<SolidColorBrush Color="{DynamicResource BoutonMarge}"/>
</TextBox.BorderBrush>
</TextBox>
</Grid>
and here's the code behind:
private void editorAddInput_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
string lastInput;
List<string> inputList = editorAddInput.Text.ToUpper().Split(',', ';').ToList();
if (inputList.Count != 0)
{
lastInput = inputList[inputList.Count - 1];
}
else
{
lastInput = editorAddInput.Text;
}
List<Editor> matchingEditorsList = new List<Editor>();
EditorPopupStackPanel.Children.Clear();
foreach(Editor editor in localEditorsList)//look up among all known names
{
if(editor.Name.StartsWith(lastInput))
{
matchingEditorsList.Add(editor);
}
}
if(matchingEditorsList.Count!=0)
{
EditorPopup.Visibility = Visibility.Visible;
foreach(Editor editor in matchingEditorsList)
{
EditorPopupStackPanel.Children.Add(new TextBlock() { Text = editor.Name });
}
EditorPopup.StaysOpen = true;
EditorPopup.IsOpen = true;
}
else
{
EditorPopup.Visibility = Visibility.Collapsed;
EditorPopup.IsOpen = false;
}
}
The input Textbox is supposed to be able to get several names, separated by a ";", so I start by getting the last one being typed.
Funny enough, the popup does appear in Visual Studio's Conceptor view when selected, but not when running. I've tried playing with z-index with no success. Any idea on what i messed up?
Put textbox inside grid in XAML
You are missing the IsOpen property, which should be set to true for the popup to be displayed.
I have the following XAML code, which creates two stack panels within a big parent Stack Panel. I would like to be able to drag each small stack panel within the parent bigStack panel.
XAML
<StackPanel BorderThickness="1" BorderBrush="Black" x:Name="bigStack">
<StackPanel x:Name="smallStack1" ManipulationMode="All" ManipulationDelta="objectManipulationDelta" ManipulationStarting="objectManipulationStarting">
<TextBlock Text="John Doe"/>
<TextBlock Text="CEO"/>
</StackPanel>
<StackPanel x:Name="smallStack2" ManipulationMode="All" ManipulationDelta="objectManipulationDelta" ManipulationStarting="objectManipulationStarting">
<TextBlock Text="Jane Doe"/>
<TextBlock Text="CTO"/>
</StackPanel>
</StackPanel>
C# backend:
private TranslateTransform dragtranslation ;
private void objectManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
dragtranslation.X += e.Delta.Translation.X;
dragtranslation.Y += e.Delta.Translation.Y;
}
private void objectManipulationStarting(object sender, ManipulationStartingRoutedEventArgs e)
{
var stackDragged = e.OriginalSource as StackPanel;
dragtranslation = new TranslateTransform();
stackDragged.RenderTransform = this.dragtranslation ;
}
Original Code found here (Official Microsoft UWP Documentation) but adapted (obviously wrongly) to suit my needs
PROBLEM 1
1) Drag smallStack1 for the first time: OK
2) Drag smallStack2 for the second time: Reverts back to the original position
PROBLEM 2
1) Drag smallStack1 for the first time: OK
2) Drag smallStack2 for the first time: OK
3) Drag either of the smallStacks again: Reverts back to the original position
You can check the problems in the .gif below:
WHAT I WISH TO ACCOMPLISH
Drag the controls using a common method, because I plan to dynamically create more controls inside the bigStack panel.
You are basically reinstantiating TranslateTransform everytime you click on an Item. That is the reason why when you click on the item second time, it navigates back to 0,0 which is the original position for TranslateTransform.
To Handle this in a easier way, this is what I would do.
1) I would give explicit TranslateTransform to the smallStackPanel's
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel BorderThickness="1" BorderBrush="Black" x:Name="bigStack">
<StackPanel x:Name="smallStack1" ManipulationMode="All" ManipulationDelta="objectManipulationDelta" >
<StackPanel.RenderTransform>
<TranslateTransform />
</StackPanel.RenderTransform>
<TextBlock Text="John Doe"/>
<TextBlock Text="CEO"/>
</StackPanel>
<StackPanel x:Name="smallStack2" ManipulationMode="All" ManipulationDelta="objectManipulationDelta" >
<StackPanel.RenderTransform>
<TranslateTransform />
</StackPanel.RenderTransform>
<TextBlock Text="Jane Doe"/>
<TextBlock Text="CTO"/>
</StackPanel>
</StackPanel>
</Grid>
And then all i need to do in codebehind is handle ManipulationDelta.
private void objectManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
var stackDragged = e.OriginalSource as StackPanel;
(stackDragged.RenderTransform as TranslateTransform).X += e.Delta.Translation.X;
(stackDragged.RenderTransform as TranslateTransform).Y += e.Delta.Translation.Y;
}
Output:
Update
To add TranslateTransform from Code
StackPanel sp = new StackPanel();
sp.RenderTransform = new TranslateTransform();
Good Luck.
I'm using PointerEntered and PointerExited on my grid to change it's color when pointer is inside, and sometimes (most of time) PointerExited does not trigger on my w10m phone, I used break point to check that. Same with Poiner(Canceled/CaptureLost). It doesn't trigger even if I touch outside the grid, when pointer is 100% outside.
Any ideas how to fix that?
My code (if needed):
private void ButtonPointerEntered(object sender, PointerRoutedEventArgs e)
{
var c = (ButtonGrid.Background as SolidColorBrush).Color;
ButtonGrid.Background = new SolidColorBrush(Color.FromArgb(60, c.R, c.G, c.B));
}
private void ButtonPointerExited(object sender, PointerRoutedEventArgs e) //Does not trigger
{
var c = (ButtonGrid.Background as SolidColorBrush).Color;
ButtonGrid.Background = new SolidColorBrush(Color.FromArgb(0, c.R, c.G, c.B));
}
XAML:
<Grid PointerEntered="ButtonPointerEntered" Tapped="ButtonTapped" x:Name="ButtonGrid" Background="{ThemeResource ApplicationForegroundThemeBrush}" PointerExited="ButtonPointerExited">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock x:Name="IconTB" FontFamily="Segoe MDL2 Assets" FontSize="18" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,3,0,0"/>
<TextBlock x:Name="TextTB" Grid.Row="1" FontSize="13" HorizontalAlignment="Center" TextWrapping="WrapWholeWords" TextAlignment="Center" Margin="0,0,0,2"/>
</Grid>
I started my app without debugging with visual studio and... It work 100% correct. So the problem was in debugging
Basically, I'm trying to make the canvas listen for a touch input (tap) and will increment the number of taps on screen. It isn't working when I touch the screen on my device. I debugged my code and nothing seems out of the ordinary except that the touch is not detected. I checked ZIndex and the canvas is in front of the screen to be touchable. How do I make it work?
XAML:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<TextBlock Name="counter" FontSize="150" HorizontalAlignment="Center" TextWrapping="Wrap" Text="0" VerticalAlignment="Center" Margin="188,10,187,397"/>
<Button Content="Reset" HorizontalAlignment="Stretch" Margin="-18,535,-18,0" VerticalAlignment="Top" Click="Button_Click"/>
<Canvas ZIndex="0" Name="Canvas" HorizontalAlignment="Center" Height="535" VerticalAlignment="Top" Width="446" MouseLeftButtonDown="Canvas_MouseLeftButtonDown" MouseLeftButtonUp="Canvas_MouseLeftButtonUp" MouseLeave="Canvas_MouseLeave"/>
</Grid>
C#:
int taps = 0; // create var to detect number of times, user touches the screen
// Constructor
public MainPage()
{
InitializeComponent();
}
// method to register the touch as the finger is placed on the screen
private void Canvas_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
//Canvas c = sender as Canvas;
counter.Text = "TOUCHED!";
}
//method register the touch as the finger is lifting up from the screen
private void Canvas_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
//Canvas c = sender as Canvas;
taps++;
counter.Text = taps.ToString(); //convert var from int to string
}
//method register the touch as the finger leaves the area of the screen
private void Canvas_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
//Canvas c = sender as Canvas;
MessageBox.Show("You left the screen without lifting your finger. That does not count as a tap!", "Caution!", MessageBoxButton.OK);
}
// method to reset the counter to zero when button is pressed and released
private void Button_Click(object sender, RoutedEventArgs e)
{
taps = 0; // reset the count
counter.Text = taps.ToString(); // convert var from int to string
}
I don't know why you want to do it with Canvas - it won't work as you have nothing in this Canvas, so it can't register your click/tap, Canvas is also hard to adjust to screen. I think it can be done simpler way if you want to do it with MouseUp/Down - subscribe directly to Grid containing your elements instead of filling this Grid with additional Canvas:
In XAML:
<Grid x:Name="ContentPanel" Margin="12,0,12,0" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="7*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<TextBlock Name="counter" FontSize="150" HorizontalAlignment="Center" TextWrapping="Wrap" Text="0" VerticalAlignment="Center" Grid.Row="0"/>
<Button Content="Reset" HorizontalAlignment="Stretch" VerticalAlignment="Center" Click="Button_Click" Grid.Row="1"/>
<TextBlock Name="Touched" FontSize="50" HorizontalAlignment="Center" TextWrapping="Wrap" Text="Touched" VerticalAlignment="Center" Visibility="Collapsed" Grid.Row="2"/>
</Grid>
In code behind:
private int taps = 0;
public MainPage()
{
InitializeComponent();
ContentPanel.MouseLeftButtonDown += ContentPanel_MouseLeftButtonDown;
ContentPanel.MouseLeftButtonUp += ContentPanel_MouseLeftButtonUp;
}
private void ContentPanel_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
taps++;
counter.Text = taps.ToString(); //convert var from int to string
Touched.Visibility = Visibility.Collapsed;
}
private void ContentPanel_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
Touched.Visibility = Visibility.Visible;
}
// method to reset the counter to zero when button is pressed and released
private void Button_Click(object sender, RoutedEventArgs e)
{
taps = 0; // reset the count
counter.Text = taps.ToString(); // convert var from int to string
}
As you can see I've subscribed to Grid events (which covers whole screen) - but to make it work I had to set its Background Brush to Transparent, otherwise it will work only if you touch text.
There are many other ways to make your App work, but I hope this will help.
Is there a reason why you don't use the touch-events?
Instead of using MouseLeftButtonDown and MouseLeftButtonUp you should use TouchDown and TouchUp.
Only when you don't handle the touch events or the manipulation events they will be mapped to mouse events. In my experience with touch a single tap also not always gets mapped to MouseLeftButtonDown. As far as I know you could also with mouse events only recoginse one finger. When you want to count more fingers it's necessary to use the TouchDown/TouchUp events.
The problem lies in the overlapping style of the grid
so either make grid rows or define a stackpanel inside the grid, something like this.
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<stackpanel>
<TextBlock Name="counter" FontSize="150" HorizontalAlignment="Center" TextWrapping="Wrap" Text="0" Margin="0,0,0,0"/>
<Canvas ZIndex="0" Name="Canvas" HorizontalAlignment="Center" Height="535" VerticalAlignment="Top" Width="446" MouseLeftButtonDown="Canvas_MouseLeftButtonDown" MouseLeftButtonUp="Canvas_MouseLeftButtonUp" MouseLeave="Canvas_MouseLeave"/>
<Button Content="Reset" HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Top" Click="Button_Click"/>
</stackpanel>
</Grid>
Try and check now.
You should set your Background property. If you don't want any background set it to Transparent:
<Canvas ZIndex="99" Background="Transparent" Name="Canvas" HorizontalAlignment="Center" Height="535" VerticalAlignment="Top" Width="446" Tapped="Canvas_CountMyTaps"/>
(If you want the canvas to be on Top be sure to make it have a greater ZIndex than the other elements that it overlaps)
If not set (the default value is null) the element won't capture any taps/click etc, it will be as if they "fall through".
Also, consider using the Tapped event which is a "higher level" event that will respond to clicks, taps with the finger, stylus, etc.