# NetHalt - NSIS installer script
# Copyright (C) 2008 Daniel Collins <solemnwarning@solemnwarning.net>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# * Neither the name of the author nor the names of its contributors may
# be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
!include MUI2.nsh
!include LogicLib.nsh
!include nsDialogs.nsh
Name "NetHalt"
OutFile "nhclient.exe"
InstallDir $PROGRAMFILES\NetHalt
InstallDirRegKey HKLM "SOFTWARE\NetHalt" "InstallDir"
!define MUI_ABORTWARNING
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "COPYING"
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_LANGUAGE "English"
Function .onInit
nsisos::osversion
${If} $0 < 5
MessageBox MB_OK "Windows 2000 (NT 5.0) or greater is required"
Abort
${EndIf}
FunctionEnd
# Do local install
#
Section "NetHalt"
SetShellVarContext all
SetOutPath $INSTDIR
# Stop all running nhtray.exe processes
#
StrCpy $0 "nhtray.exe"
KillProc::KillProcesses
# Check if the NetHalt service is installed
#
SimpleSC::ExistsService "nhclient"
Pop $0
# Stop+Remove the NetHalt service if it's installed
#
${If} $0 == 0
DetailPrint "Stopping NetHalt Client service..."
SimpleSC::StopService "nhclient"
DetailPrint "Removing NetHalt Client service..."
SimpleSC::RemoveService "nhclient"
${EndIf}
WriteRegStr HKLM "SOFTWARE\NetHalt" "InstallDir" $INSTDIR
cinst::reg_write "dword" "SOFTWARE\NetHalt" "use_server" "0"
cinst::reg_write "string" "SOFTWARE\NetHalt" "server_name" ""
cinst::reg_write "dword" "SOFTWARE\NetHalt" "server_port" "0"
cinst::reg_write "dword" "SOFTWARE\NetHalt" "server_refresh" "1800"
cinst::reg_write "dword" "SOFTWARE\NetHalt" "warning" "300"
cinst::reg_write "dword" "SOFTWARE\NetHalt" "abort" "0"
cinst::reg_write "dword" "SOFTWARE\NetHalt" "delay" "0"
cinst::reg_write "string" "SOFTWARE\NetHalt" "sdtimes" ""
WriteUninstaller "$INSTDIR\uninstall.exe"
File "src\nhclient.exe"
File "src\evlog.dll"
File "src\nhtray.exe"
File "src\nhconfig.exe"
# Add the event log source (evlog.dll) to the registry
#
WriteRegStr HKLM "SYSTEM\CurrentControlSet\Services\Eventlog\Application\NetHalt Client" "EventMessageFile" "$INSTDIR\evlog.dll"
WriteRegDWORD HKLM "SYSTEM\CurrentControlSet\Services\Eventlog\Application\NetHalt Client" "TypesSupported" 0x00000007
# Add the uninstaller to Add/Remove programs
#
WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\NetHalt" "DisplayName" "NetHalt"
WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\NetHalt" "UninstallString" "$INSTDIR\uninstall.exe"
WriteRegDWORD HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\NetHalt" "NoModify" 1
WriteRegDWORD HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\NetHalt" "NoRepair" 1
# Install and start the NetHalt service
# TODO: Check for errors
#
DetailPrint "Installing NetHalt Client service..."
SimpleSC::InstallService "nhclient" "NetHalt Client" "16" "2" "$INSTDIR\nhclient.exe" "" "" ""
DetailPrint "Starting NetHalt Client service..."
SimpleSC::StartService "nhclient"
# Add nhtray.exe to the registry to run at login
#
WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" "NetHalt" "$INSTDIR\nhtray.exe"
# Launch nhtray.exe
#
Exec '"$INSTDIR\nhtray.exe"'
# Create shortcuts
#
CreateDirectory "$SMPROGRAMS\NetHalt"
CreateShortCut "$SMPROGRAMS\NetHalt\Tray Icon.lnk" "$INSTDIR\nhtray.exe"
CreateShortCut "$SMPROGRAMS\NetHalt\Configuration.lnk" "$INSTDIR\nhconfig.exe"
CreateShortCut "$SMPROGRAMS\NetHalt\Uninstall.lnk" "$INSTDIR\uninstall.exe"
SectionEnd
Function un.onInit
SetShellVarContext all
ReadRegStr $INSTDIR HKLM "SOFTWARE\NetHalt" "InstallDir"
MessageBox MB_YESNO "This will uninstall NetHalt, continue?" IDYES NoAbort
Abort
NoAbort:
FunctionEnd
Section "Uninstall"
# Stop and remove the NetHalt service
#
DetailPrint "Stopping NetHalt Client service..."
SimpleSC::StopService "nhclient"
DetailPrint "Removing NetHalt Client service..."
SimpleSC::RemoveService "nhclient"
# Stop all running nhtray.exe processes
#
StrCpy $0 "nhtray.exe"
KillProc::KillProcesses
# Delete shortcuts
#
Delete "$SMPROGRAMS\NetHalt\Tray Icon.lnk"
Delete "$SMPROGRAMS\NetHalt\Configuration.lnk"
Delete "$SMPROGRAMS\NetHalt\Un-Install.lnk"
Delete "$SMPROGRAMS\NetHalt"
DeleteRegValue HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" "NetHalt"
DeleteRegKey HKLM "SYSTEM\CurrentControlSet\Services\Eventlog\Application\NetHalt Client"
DeleteRegKey HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\NetHalt"
Delete "$INSTDIR\nhclient.exe"
Delete "$INSTDIR\evlog.dll"
Delete "$INSTDIR\nhtray.exe"
Delete "$INSTDIR\uninstall.exe"
RMDir $INSTDIR
SectionEnd
NSIS实现安装前先停止并卸载旧版
以下脚本会先找到正在运行的程序和服务,停止并删除服务,然后杀掉正在运行的程序进程
;检查服务是否存在
SimpleSC::ExistsService "${SVCHOST_EXE_NAME}"
Pop $0
;停止并删除服务
${If} $0 == 0
DetailPrint "停止正在运行的打印服务"
SimpleSC::StopService "${SVCHOST_EXE_NAME}" 1 30
DetailPrint "删除已安装的打印服务"
SimpleSC::RemoveService "${SVCHOST_EXE_NAME}"
${EndIf}
;检查主程序是否运行,如果正在运行则进行终止
nsProcess::_FindProcess "${ANALYST_EXE_NAME}"
Pop $R0
${If} $R0 = 0
DetailPrint "停止正在运行的主程序"
nsProcess::_KillProcess "${ANALYST_EXE_NAME}"
Pop $R0
${EndIf}
;检查升级程序是否运行,如果正在运行则进行终止
nsProcess::_FindProcess "${UPDATE_EXE_NAME}"
Pop $R0
${If} $R0 = 0
DetailPrint "停止正在运行的升级程序"
nsProcess::_KillProcess "${UPDATE_EXE_NAME}"
Pop $R0
${EndIf}
Django admin export inline sub models
Django 导入导出大部分情况都可以使用django-import-export
扩展,但是实际业务中经常需要导出子模型数据,比如导出订单同时需要导出订单商品信息。django-import-export
扩展默认不支持导出inline子模型,这时候需要自己实现resource的export方法:
自定义django-import-export
的export方法
class OrderResource(resources.ModelResource):
class Meta:
model = Order
def export(self, queryset=None, *args, **kwargs):
if queryset is None:
queryset = self.get_queryset()
ds = tablib.Dataset()
data = []
for order in queryset:
for item in order.items.all():
row = {
'order_id': order.id,
"item_id": item.idd,
}
data.append(row)
ds.dict = data
return ds
参考
JSON、BSON及Protocol Buffer比较
他们核心区别在于,bson在json基础上增加了数据日期、文档类型,并支持二进制传输;而protocol buffer则对传输数据和结构定义进行了分离。一般来说与浏览器通信通常使用json,如果需要跨语言通信,且考虑效率,则可以采用protocol buffer。
JSON
特点:
文本存储和传输,结构与数据一体,肉眼可识别
优点:
- 通用性好,各种语言都支持;
- 可用于存储
缺点:
- 采用文本传输,占用体积大,传输性能差
- 序列化,反序列化性能较差
- 不支持二进制,日期等数据类型
Bson
特点:
采用二进制传输,结构与数据一体
优点:
- 在json基础上增加二进制和日期等格式;
- 传输性能较好
- 序列化,反序列化性能较好
缺点:
- 多语言支持一般
- 传输数据与结构定义仍然在一体
Protocol Buffer
特点:
结构定义与传输数据分离,只传输二进制数据
优点:
- 用protoc工具可以根据proto文件生成各种语言对应代码
- 字段被编号,新添加的字段不影响老结构,解决了向后兼容问题。
- 二进制无结构消息,效率高,性能高。
缺点:
- 二进制格式,可读性差(抓包dump后的数据很难看懂)
- 默认不具备动态特性
django-q — 比django-celery和django-rq更简单又不失强大的django队列
django-q介绍
Django Q is a native Django task queue, scheduler and worker application using Python multiprocessing.
Features
- Multiprocessing worker pools
- Asynchronous tasks
- Scheduled, cron and repeated tasks
- Signed and compressed packages
- Failure and success database or cache
- Result hooks, groups and chains
- Django Admin integration
- PaaS compatible with multiple instances
- Multi cluster monitor
- Redis, Disque, IronMQ, SQS, MongoDB or ORM
- Rollbar and Sentry support
Django Q is tested with: Python 3.7 and 3.8, Django 2.2.x and 3.1.x
安装django-q模块
pip install django-q
增加 django_q 到 INSTALLED_APPS:
修改settings.py
INSTALLED_APPS = (
# other apps
'django_q',
)
创建数据库表:
运行 Django migrations
$ python manage.py migrate
配置一个broker
使用django orm数据库作为broker
Q_CLUSTER = {
'name': 'DjangORM',
'workers': 1,
'timeout': 90,
'retry': 120,
'queue_limit': 50,
'bulk': 10,
'orm': 'default'
}
使用redis作为broker
Q_CLUSTER = {
'redis': 'redis://h:asdfqwer1234asdf@ec2-111-1-1-1.compute-1.amazonaws.com:111'
}
使用django_redis作为broker
Q_CLUSTER = {
'name': 'DJRedis',
'workers': 4,
'timeout': 90,
'django_redis': 'default'
}
其他broker: https://django-q.readthedocs.io/en/latest/brokers.html
使用qcluster处理异步任务
python manage.py qcluster
增加异步任务到队列
from django_q.tasks import async_task, result
# create the task
async_task('math.copysign', 2, -2)
# or with import and storing the id
import math.copysign
task_id = async_task(copysign, 2, -2)
# get the result
task_result = result(task_id)
# result returns None if the task has not been executed yet
# you can wait for it
task_result = result(task_id, 200)
# but in most cases you will want to use a hook:
async_task('math.modf', 2.5, hook='hooks.print_result')
# hooks.py
def print_result(task):
print(task.result)
管理后台页面
Django Q不使用自定义页面,而是默认使用Django模型管理员提供的功能。当您打开Django Q的管理页面时,您将看到三种模型:
成功的任务
意味着他们在执行过程中没有遇到任何错误。
失败的任务
失败的任务遇到错误,阻止其完成执行。
计划任务
在这里,您可以检查计划任务的状态,创建,编辑或删除它们。
排队的任务
仅当您使用Django ORM代理时才出现
参考
NSIS递归添加和删除子目录及文件
NSIS打包比较配置文件手动编辑起来非常麻烦,使用HM NIS EDIT
向导的话就容易多了。
添加目录以及子目录文件
File /r "bin\Release\*.*"
删除目录以及子目录
RMDir /r "$INSTDIR"
参考
使用AutoUpdater.NET实现WPF和Winfoms项目自动更新
使用AutoUpdater.NET实现自动更新
AutoUpdater.NET是一个.net库,允许winforms和wpf应用实现自动更新,只需要以下几步:
1,定义一个升级文件;
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>2.0.0.0</version>
<url>http://rbsoft.org/downloads/AutoUpdaterTest.zip</url>
<changelog>https://github.com/ravibpatel/AutoUpdater.NET/releases</changelog>
<mandatory>false</mandatory>
</item>
2, 安装 Autoupdater.NET.Official扩展
PM> Install-Package Autoupdater.NET.Official
3,使用只需要在程序加载前加入以下代码即可
protected override void OnStartup(StartupEventArgs e)
{
AutoUpdater.ParseUpdateInfoEvent += AutoUpdaterOnParseUpdateInfoEvent;
//AutoUpdater.ReportErrors = true;
AutoUpdater.Start("https://api.yunyin.la/file-helper/upgrade");
}
将xml修改为json
private void AutoUpdaterOnParseUpdateInfoEvent(ParseUpdateInfoEventArgs args)
{
dynamic json = JsonConvert.DeserializeObject(args.RemoteData);
args.UpdateInfo = new UpdateInfoEventArgs
{
CurrentVersion = json.version,
DownloadURL = json.url,
Mandatory = new Mandatory
{
Value = json.mandatory.value,
UpdateMode = json.mandatory.mode,
}
};
}
json样例
{
"version":"2.0.0.0",
"url":"http://rbsoft.org/downloads/AutoUpdaterTest.zip",
"changelog":"https://github.com/ravibpatel/AutoUpdater.NET/releases",
"mandatory":{
"value":true,
"minVersion": "2.0.0.0",
"mode":1
},
"checksum":{
"value":"E5F59E50FC91A9E52634FFCB11F32BD37FE0E2F1",
"hashingAlgorithm":"SHA1"
}
}
参考
WPF点击关闭按钮最小化到系统托盘
包含点击关闭按钮后最小化系统托盘,点击系统托盘图标后自动弹出窗口,点击托盘右键菜单退出
public partial class MainWindow : Window
{
private readonly System.Windows.Forms.NotifyIcon nIcon = new System.Windows.Forms.NotifyIcon();
public MainWindow()
{
InitializeComponent();
initNotifyIcon();
}
private void initNotifyIcon()
{
nIcon.Visible = true;
nIcon.Icon = new Icon("./favicon.ico");
nIcon.Text = "测试程序";
nIcon.MouseClick += new System.Windows.Forms.MouseEventHandler(show_Click);
nIcon.ContextMenu = new System.Windows.Forms.ContextMenu();
System.Windows.Forms.MenuItem show = new System.Windows.Forms.MenuItem("打开");
show.Click += new EventHandler(show_Click);
nIcon.ContextMenu.MenuItems.Add(show);
System.Windows.Forms.MenuItem exit = new System.Windows.Forms.MenuItem("退出");
exit.Click += new EventHandler(exit_Click);
nIcon.ContextMenu.MenuItems.Add(exit);
}
private void exit_Click(object sender, EventArgs e)
{
Environment.Exit(0);
}
private void show_Click(object Sender, EventArgs e)
{
if (WindowState == WindowState.Minimized)
WindowState = WindowState.Normal;
Show();
Activate();
}
protected override void OnStateChanged(EventArgs e)
{
if (WindowState == WindowState.Minimized) Hide();
base.OnStateChanged(e);
}
protected override void OnClosing(CancelEventArgs e)
{
e.Cancel = true;
Hide();
base.OnClosing(e);
}
}
参考
WPF自定义滚动条(含windows和apple两种风格)
测试结构
<Grid>
<ListView VerticalAlignment="Top" HorizontalAlignment="Stretch">
<ListViewItem>
<TextBlock Text="xxxx1"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx2"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx3"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx4"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx5"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx6"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx7"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx8"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx9"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx10"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx11"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx12"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx13"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx14"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx15"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx16"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx17"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx18"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx19"/>
</ListViewItem>
<ListViewItem>
<TextBlock Text="xxxx20"/>
</ListViewItem>
</ListView>
</Grid>
Windows 风格
<Style x:Key="ScrollBarLineButton"
TargetType="{x:Type RepeatButton}">
<Setter Property="SnapsToDevicePixels"
Value="True" />
<Setter Property="OverridesDefaultStyle"
Value="true" />
<Setter Property="Focusable"
Value="false" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RepeatButton}">
<Border x:Name="Border"
Margin="1"
CornerRadius="2"
BorderThickness="1">
<Border.BorderBrush>
<LinearGradientBrush StartPoint="0,0"
EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="{DynamicResource BorderMediumColor}"
Offset="0.0" />
<GradientStop Color="{DynamicResource BorderDarkColor}"
Offset="1.0" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.BorderBrush>
<Border.Background>
<LinearGradientBrush StartPoint="0,0"
EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="{DynamicResource ControlLightColor}"/>
<GradientStop Color="{DynamicResource ControlMediumColor}"
Offset="1.0" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.Background>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver" />
<VisualState x:Name="Pressed">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Border"
Storyboard.TargetProperty="(Panel.Background).
(GradientBrush.GradientStops)[1].(GradientStop.Color)">
<EasingColorKeyFrame KeyTime="0"
Value="{StaticResource ControlPressedColor}" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Arrow"
Storyboard.TargetProperty="(Shape.Fill).
(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="0"
Value="{StaticResource DisabledForegroundColor}" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Path x:Name="Arrow"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="{Binding Content,
RelativeSource={RelativeSource TemplatedParent}}" >
<Path.Fill>
<SolidColorBrush Color="{DynamicResource GlyphColor}"/>
</Path.Fill>
</Path>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ScrollBarPageButton"
TargetType="{x:Type RepeatButton}">
<Setter Property="SnapsToDevicePixels"
Value="True" />
<Setter Property="OverridesDefaultStyle"
Value="true" />
<Setter Property="IsTabStop"
Value="false" />
<Setter Property="Focusable"
Value="false" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RepeatButton}">
<Border Background="Transparent" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ScrollBarThumb"
TargetType="{x:Type Thumb}">
<Setter Property="SnapsToDevicePixels"
Value="True" />
<Setter Property="OverridesDefaultStyle"
Value="true" />
<Setter Property="IsTabStop"
Value="false" />
<Setter Property="Focusable"
Value="false" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Border CornerRadius="2"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="1" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ControlTemplate x:Key="VerticalScrollBar"
TargetType="{x:Type ScrollBar}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition MaxHeight="18" />
<RowDefinition Height="0.00001*" />
<RowDefinition MaxHeight="18" />
</Grid.RowDefinitions>
<Border Grid.RowSpan="3"
CornerRadius="2"
Background="#F0F0F0" />
<RepeatButton Grid.Row="0"
Style="{StaticResource ScrollBarLineButton}"
Height="18"
Command="ScrollBar.LineUpCommand"
Content="M 0 4 L 8 4 L 4 0 Z" />
<Track x:Name="PART_Track"
Grid.Row="1"
IsDirectionReversed="true">
<Track.DecreaseRepeatButton>
<RepeatButton Style="{StaticResource ScrollBarPageButton}"
Command="ScrollBar.PageUpCommand" />
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb Style="{StaticResource ScrollBarThumb}"
Margin="1,0,1,0">
<Thumb.BorderBrush>
<LinearGradientBrush StartPoint="0,0"
EndPoint="1,0">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="{DynamicResource BorderLightColor}"
Offset="0.0" />
<GradientStop Color="{DynamicResource BorderDarkColor}"
Offset="1.0" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Thumb.BorderBrush>
<Thumb.Background>
<LinearGradientBrush StartPoint="0,0"
EndPoint="1,0">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="{DynamicResource ControlLightColor}"
Offset="0.0" />
<GradientStop Color="{DynamicResource ControlMediumColor}"
Offset="1.0" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Thumb.Background>
</Thumb>
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton Style="{StaticResource ScrollBarPageButton}"
Command="ScrollBar.PageDownCommand" />
</Track.IncreaseRepeatButton>
</Track>
<RepeatButton Grid.Row="2"
Style="{StaticResource ScrollBarLineButton}"
Height="18"
Command="ScrollBar.LineDownCommand"
Content="M 0 0 L 4 4 L 8 0 Z" />
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="HorizontalScrollBar"
TargetType="{x:Type ScrollBar}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MaxWidth="18" />
<ColumnDefinition Width="0.00001*" />
<ColumnDefinition MaxWidth="18" />
</Grid.ColumnDefinitions>
<Border Grid.ColumnSpan="3"
CornerRadius="2"
Background="#F0F0F0" />
<RepeatButton Grid.Column="0"
Style="{StaticResource ScrollBarLineButton}"
Width="18"
Command="ScrollBar.LineLeftCommand"
Content="M 4 0 L 4 8 L 0 4 Z" />
<Track x:Name="PART_Track"
Grid.Column="1"
IsDirectionReversed="False">
<Track.DecreaseRepeatButton>
<RepeatButton Style="{StaticResource ScrollBarPageButton}"
Command="ScrollBar.PageLeftCommand" />
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb Style="{StaticResource ScrollBarThumb}"
Margin="0,1,0,1">
<Thumb.BorderBrush>
<LinearGradientBrush StartPoint="0,0"
EndPoint="1,0">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="{DynamicResource BorderLightColor}"
Offset="0.0" />
<GradientStop Color="{DynamicResource BorderDarkColor}"
Offset="1.0" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Thumb.BorderBrush>
<Thumb.Background>
<LinearGradientBrush StartPoint="0,0"
EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="{DynamicResource ControlLightColor}"
Offset="0.0" />
<GradientStop Color="{DynamicResource ControlMediumColor}"
Offset="1.0" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Thumb.Background>
</Thumb>
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton Style="{StaticResource ScrollBarPageButton}"
Command="ScrollBar.PageRightCommand" />
</Track.IncreaseRepeatButton>
</Track>
<RepeatButton Grid.Column="2"
Style="{StaticResource ScrollBarLineButton}"
Width="18"
Command="ScrollBar.LineRightCommand"
Content="M 0 0 L 4 4 L 0 8 Z" />
</Grid>
</ControlTemplate>
<Style x:Key="{x:Type ScrollBar}"
TargetType="{x:Type ScrollBar}">
<Setter Property="SnapsToDevicePixels"
Value="True" />
<Setter Property="OverridesDefaultStyle"
Value="true" />
<Style.Triggers>
<Trigger Property="Orientation"
Value="Horizontal">
<Setter Property="Width"
Value="Auto" />
<Setter Property="Height"
Value="18" />
<Setter Property="Template"
Value="{StaticResource HorizontalScrollBar}" />
</Trigger>
<Trigger Property="Orientation"
Value="Vertical">
<Setter Property="Width"
Value="18" />
<Setter Property="Height"
Value="Auto" />
<Setter Property="Template"
Value="{StaticResource VerticalScrollBar}" />
</Trigger>
</Style.Triggers>
</Style>
需要的资源
<!--Control colors.-->
<Color x:Key="WindowColor">#FFE8EDF9</Color>
<Color x:Key="ContentAreaColorLight">#FFC5CBF9</Color>
<Color x:Key="ContentAreaColorDark">#FF7381F9</Color>
<Color x:Key="DisabledControlLightColor">#FFE8EDF9</Color>
<Color x:Key="DisabledControlDarkColor">#FFC5CBF9</Color>
<Color x:Key="DisabledForegroundColor">#FF888888</Color>
<Color x:Key="SelectedBackgroundColor">#FFC5CBF9</Color>
<Color x:Key="SelectedUnfocusedColor">#FFDDDDDD</Color>
<Color x:Key="ControlLightColor">White</Color>
<Color x:Key="ControlMediumColor">#FF7381F9</Color>
<Color x:Key="ControlDarkColor">#FF211AA9</Color>
<Color x:Key="ControlMouseOverColor">#FF3843C4</Color>
<Color x:Key="ControlPressedColor">#FF211AA9</Color>
<Color x:Key="GlyphColor">#FF444444</Color>
<Color x:Key="GlyphMouseOver">sc#1, 0.004391443, 0.002428215, 0.242281124</Color>
<!--Border colors-->
<Color x:Key="BorderLightColor">#FFCCCCCC</Color>
<Color x:Key="BorderMediumColor">#FF888888</Color>
<Color x:Key="BorderDarkColor">#FF444444</Color>
<Color x:Key="PressedBorderLightColor">#FF888888</Color>
<Color x:Key="PressedBorderDarkColor">#FF444444</Color>
<Color x:Key="DisabledBorderLightColor">#FFAAAAAA</Color>
<Color x:Key="DisabledBorderDarkColor">#FF888888</Color>
<Color x:Key="DefaultBorderBrushDarkColor">Black</Color>
<!--Control-specific resources.-->
<Color x:Key="HeaderTopColor">#FFC5CBF9</Color>
<Color x:Key="DatagridCurrentCellBorderColor">Black</Color>
<Color x:Key="SliderTrackDarkColor">#FFC5CBF9</Color>
<Color x:Key="NavButtonFrameColor">#FF3843C4</Color>
<LinearGradientBrush x:Key="MenuPopupBrush"
EndPoint="0.5,1"
StartPoint="0.5,0">
<GradientStop Color="{DynamicResource ControlLightColor}"
Offset="0" />
<GradientStop Color="{DynamicResource ControlMediumColor}"
Offset="0.5" />
<GradientStop Color="{DynamicResource ControlLightColor}"
Offset="1" />
</LinearGradientBrush>
<LinearGradientBrush x:Key="ProgressBarIndicatorAnimatedFill"
StartPoint="0,0"
EndPoint="1,0">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#000000FF"
Offset="0" />
<GradientStop Color="#600000FF"
Offset="0.4" />
<GradientStop Color="#600000FF"
Offset="0.6" />
<GradientStop Color="#000000FF"
Offset="1" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
Apple风格
<!--Scrollbar Thumbs-->
<Style x:Key="ScrollThumbs" TargetType="{x:Type Thumb}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Grid x:Name="Grid">
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Width="Auto" Height="Auto" Fill="Transparent" />
<Border x:Name="Rectangle1" CornerRadius="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Width="Auto" Height="Auto" Background="{TemplateBinding Background}" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Tag" Value="Horizontal">
<Setter TargetName="Rectangle1" Property="Width" Value="Auto" />
<Setter TargetName="Rectangle1" Property="Height" Value="7" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--ScrollBars-->
<Style x:Key="{x:Type ScrollBar}" TargetType="{x:Type ScrollBar}">
<Setter Property="Stylus.IsFlicksEnabled" Value="false" />
<Setter Property="Foreground" Value="#8C8C8C" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Width" Value="8" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollBar}">
<Grid x:Name="GridRoot" Width="8" Background="{TemplateBinding Background}">
<Grid.RowDefinitions>
<RowDefinition Height="0.00001*" />
</Grid.RowDefinitions>
<Track x:Name="PART_Track" Grid.Row="0" IsDirectionReversed="true" Focusable="false">
<Track.Thumb>
<Thumb x:Name="Thumb" Background="{TemplateBinding Foreground}" Style="{DynamicResource ScrollThumbs}" />
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton x:Name="PageUp" Command="ScrollBar.PageDownCommand" Opacity="0" Focusable="false" />
</Track.IncreaseRepeatButton>
<Track.DecreaseRepeatButton>
<RepeatButton x:Name="PageDown" Command="ScrollBar.PageUpCommand" Opacity="0" Focusable="false" />
</Track.DecreaseRepeatButton>
</Track>
</Grid>
<ControlTemplate.Triggers>
<Trigger SourceName="Thumb" Property="IsMouseOver" Value="true">
<Setter Value="{DynamicResource ButtonSelectBrush}" TargetName="Thumb" Property="Background" />
</Trigger>
<Trigger SourceName="Thumb" Property="IsDragging" Value="true">
<Setter Value="{DynamicResource DarkBrush}" TargetName="Thumb" Property="Background" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="Thumb" Property="Visibility" Value="Collapsed" />
</Trigger>
<Trigger Property="Orientation" Value="Horizontal">
<Setter TargetName="GridRoot" Property="LayoutTransform">
<Setter.Value>
<RotateTransform Angle="-90" />
</Setter.Value>
</Setter>
<Setter TargetName="PART_Track" Property="LayoutTransform">
<Setter.Value>
<RotateTransform Angle="-90" />
</Setter.Value>
</Setter>
<Setter Property="Width" Value="Auto" />
<Setter Property="Height" Value="8" />
<Setter TargetName="Thumb" Property="Tag" Value="Horizontal" />
<Setter TargetName="PageDown" Property="Command" Value="ScrollBar.PageLeftCommand" />
<Setter TargetName="PageUp" Property="Command" Value="ScrollBar.PageRightCommand" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
参考
Livewire 快速上手教程
Livewire – 不用一行JS实现vue/ng/react等框架的数据绑定效果
理解Livewire
构建现代的Web应用程序很困难。 Vue和React之类的工具功能非常强大,但是它们增加了全栈开发人员工作流程的复杂性。
Livewire是Laravel的全栈框架,可简化构建交互式页面的过程。不用一行JS实现vue/ng/react等框架的数据绑定效果。
理解它的最好方法就是看代码。
下面构建一个搜索组件,当用户键入搜索输入时,用户列表将实时更新。
组件PHP代码:
use Livewire\Component;
class SearchUsers extends Component
{
public $search = '';
public function render()
{
return view('livewire.search-users', [
'users' => User::where('username', $this->search)->get(),
]);
}
}
组件视图
<div>
<input wire:model="search" type="text" placeholder="Search users..."/>
<ul>
@foreach($users as $user)
<li>{{ $user->username }}</li>
@endforeach
</ul>
</div>
引用组件
<body>
...
@livewire('search-users')
...
</body>
快速上手教程
教程非常简单,客户端点击一次按钮,实现客户端和服务端计数器同时加一。
安装 Livewire
composer require livewire/livewire
在视图中保护livewire的css和js
@livewireStyles
</head>
<body>
...
@livewireScripts
</body>
</html>
新增组件
php artisan make:livewire counter
组件文件内容
class Counter extends Component
{
public $count = 0;
public function increment()
{
$this->count++;
}
public function render()
{
return view('livewire.counter');
}
}
组件视图
<div style="text-align: center">
<button wire:click="increment">+</button>
<h1>{{ $count }}</h1>
</div>
在视图中引用组件
<head>
...
@livewireStyles
</head>
<body>
<livewire:counter />
...
@livewireScripts
</body>
</html>